分布式链路追踪之SkyWalking
一 链路追踪简介
在微服务架构中,一次请求往往涉及到多个模块,多个中间件,多台机器的相互协作才能完成。这一系列调用请求中,有些是串行的,有些是并行的,那么如何确定这个请求背后调用了哪些应用,哪些模块,哪些节点及调用的先后顺序?如何定位每个模块的性能问题?本文将为你揭晓答案。
衡量一个接口的性能好坏,一般我们至少会关注以下三个指标
- 接口的RT (响应时间) 你怎么知道?
- 是否有异常响应?
- 主要慢在哪里?
1.1 传统的单体架构
在初期,公司刚起步的时候,可能多会采用如下单体架构,对于单体架构我们该用什么方式来计算以上三个指标呢?
最容易想到的显然是用 AOP:
1.2 微服务架构
在单体架构中由于所有的服务,组件都在一台机器上,所以相对来说这些监控指标比较容易实现,不过随着业务的快速发展,单体架构必然会朝微服务架构发展,如下:
如果有用户反馈某个页面很慢,我们知道这个页面的请求调用链是 A -----> C -----> B -----> D,此时如何定位可能是哪个模块引起的问题。每个服务 Service A,B,C,D 都有好几台机器。怎么知道某个请求调用了服务的具体哪台机器呢?
如图所示可发现微服务接口调用存在以下几个问题:
- 排查问题难度大,周期长
- 特定场景难复现
- 系统性能瓶颈分析较难
分布式调用链就是为了解决以上几个问题而生,它主要的作用如下:
- 自动采取数据
- 分析数据产生完整调用链:有了请求的完整调用链,问题有很大概率可复现
- 数据可视化:每个组件的性能可视化,能帮助我们很好地定位系统的瓶颈,及时找出问题所在
通过分布式追踪系统能很好地定位如下请求的每条具体请求链路,从而轻易地实现请求链路追踪,每个模块的性能瓶颈定位与分析。
1.3 分布式调用链路标准 - openTracing
知道了分布式调用链的作用,那我们来看下如何实现分布式调用链的实现及原理, 首先为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范,OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。
这样 OpenTracing 通过提供平台无关,厂商无关的 API,使得开发人员能够方便地添加追踪系统的实现。
说到这大家是否想过 Java 中类似的实现?还记得 JDBC 吧,通过提供一套标准的接口让各个厂商去实现,程序员即可面对接口编程,不用关心具体的实现。这里的接口其实就是标准,所以制定一套标准非常重要,可以实现组件的可插拔。
接下来我们来看 OpenTracing 的数据模型,主要有以下三个
- Trace:一个完整请求链路
- Span:一次调用过程(需要有开始时间和结束时间)
- SpanContext:Trace 的全局上下文信息, 如里面有traceId
理解这三个概念非常重要,为了让大家更好地理解这三个概念,我特意画了一张图。
如图所示,一次下单的完整请求完整就是一个 Trace, 显然对于这个请求来说,必须要有一个全局标识来标识这一个请求,每一次调用就称为一个 Span,每一次调用都要带上全局的 TraceId, 这样才可把全局 TraceId 与每个调用关联起来,这个 TraceId 就是通过 SpanContext 传输的,既然要传输显然都要遵循协议来调用。如图示,我们把传输协议比作车,把 SpanContext 比作货,把 Span 比作路应该会更好理解一些。
理解了这三个概念,接下来我看看分布式追踪系统如何采集统一图中的微服务调用链。
我们可以看到底层有一个 Collector 一直在默默无闻地收集数据,那么每一次调用 Collector 会收集哪些信息呢。
- 全局 trace_id:这是显然的,这样才能把每一个子调用与最初的请求关联起来
- span_id: 图中的 0,1,1.1,2,这样就能标识是哪一个调用
- parent_span_id:比如 b 调用 d 的 span_id 是 1.1,那么它的 parent_span_id 即为 a 调用 b 的 span_id 即 1,这样才能把两个紧邻的调用关联起来。
有了这些信息,Collector 收集的每次调用的信息如下
于是一个完整的分布式追踪系统就实现了。
以上实现看起来确实简单,但有以下几个问题需要我们仔细思考一下
- 怎么自动采集 span 数据:自动采集,对业务代码无侵入
- 如何跨进程传递 context
- traceId 如何保证全局唯一
- 请求量这么多采集会不会影响性能
接下我来看看 SkyWalking 是如何解决以上四个问题的
1.4 常见的分布式链路追踪方案实现
目前市场上比较常见的链路追踪方案如下:
- Zipkin是Twitter开源的调用链分析工具,目前基于springcloud sleuth得到了广泛的使用,特点是轻量,使用部署简单。
- Pinpoint是韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能强大,接入端无代码侵入。
- SkyWalking 是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能较强,接入端无代码侵入。目前已加入Apache孵化器。
- CAT(Central Application Tracking)是大众点评开源的基于编码和配置的调用链分析,应用监控分析,日志采集,监控报警等一系列的监控平台工具。
- jaeger 受到Dapper和OpenZipkin的启发,是由Uber Technologies创建 并捐赠给Cloud Native Computing Foundation的分布式追踪平台。
类别 | Zipkin | Pinpoint | skywalking | CAT | jaeger |
---|---|---|---|---|---|
实现方式 | 拦截请求,发送(http,mq)数据到zipkin服务 | java探针,字节码增强 | java探针,字节码增强 | 代码埋点(拦截器,注解,过滤器) | 拦截请求 |
主要支持语言 | Java | Java | Java/c/c++/python/GO/Node/PHP/.NET/nginx | Java/c/c++/python/GO/node | go |
接入方式 | 基于linkerd或者sleuth方式,引入配置即可 | java agent字节码 | java agent字节码 | 代码侵入 | 拦截侵入 |
agent到collector的协议 | http,mq | thrift | gRPC | http/tcp | udp/http |
openTracing | √ | × | √ | × | √ |
颗粒度 | 接口级别 | 方法级别 | 方法级别 | 代码级别 | 接口级别 |
全局调用统计 | × | √ | √ | √ | × |
traceId查询 | √ | × | √ | × | √ |
报警 | × | √ | √ | √ | × |
JVM监控 | × | × | √ | √ | × |
健壮度 | ** | ***** | **** | ***** | **** |
数据存储 | ES,mysql,Cassandra,内存 | Hbase | ES,H2 | mysql,hdfs | ES,kafka,cassandra,内存 |
社区活跃度 | 15.2k | 12.4k | 20.2k | 17.2k | 16.3k |
PinPoint和SkyWalking支持的插件对比
类别 | Pinpoint | Skywalking |
---|---|---|
web容器 | Tomcat6/7/8,Resin,Jetty,JBoss,Websphere | Tomcat7/8/9,Resin,Jetty |
JDBC | Oracle,mysql | Oracle,mysql,Sharding-JDBC |
消息中间件 | ActiveMQ,RabbitMQ | RocketMQ 4.x,Kafka |
日志 | log4j, Logback | log4j,log4j2, Logback |
HTTP库 | Apache HTTP Client,GoogleHttpClient,OkHttpClient | Apache HTTP Client, OkHttpClient,Feign |
Spring体系 | spring,springboot | spring,springboot,eureka,hystrix |
RPC框架 | Dubbo,Thrift | Dubbo,Motan,gRPC,ServiceComb |
NOSQL | Memcached, Redis, CASSANDRA | Memcached,Redis |
二 SkyWalkking的原理和架构设计
节点数据的定时采样,采样后将数据定时上报,将其存储到 ES, MySQL 等持久化层,有了数据自然而然可根据数据做可视化分析。
2.1 skywalking的工作机制
skywalking的工作机制,需要三块协同。工作原理图大致如下:
- skywalking server:负责接收、存储并展示,所以server模块包含一个展示web子模块;
- agent:负责代理微服务并收集需要的信息,转发给server;
- 微服务本身:需要在启动时指定agent,以便生成代理类。
2.2 skywalking的核心模块
SkyWalking采用组件式开发,易于扩展,主要组件作用如下:
-
Skywalking Agent:链路数据采集tracing(调用链数据)和metric(指标)信息并上报,上报通过HTTP或者gRPC方式发送数据到Skywalking Collector
-
Skywalking Collector : 链路数据收集器,对agent传过来的tracing和metric数据进行整合分析通过Analysis Core模块处理并落入相关的数据存储中,同时会通过Query Core模块进行二次统计和监控告警
-
Storage: Skywalking的存储,支持以ElasticSearch、Mysql、TiDB、H2等主流存储作为存储介质进行数据存储,H2仅作为临时演示单机用。
-
SkyWalking UI: Web可视化平台,用来展示落地的数据,目前官方采纳了RocketBot作为SkyWalking的主UI
2.3 自动采集span数据
SkyWalking 采用了插件化 + javaagent 的形式来实现了 span 数据的自动采集,这样可以做到对代码的 无侵入性,插件化意味着可插拔,扩展性好。
2.4 如何跨进程传递 context
我们知道数据一般分为 header 和 body, 就像 http 有 header 和 body, RocketMQ 也有 MessageHeader,Message Body, body 一般放着业务数据,所以不宜在 body 中传递 context,应该在 header 中传递 context,如图示:
dubbo 中的 attachment 就相当于 header ,所以我们把 context 放在 attachment 中,这样就解决了 context 的传递问题。
2.5 traceId如何保证全局唯一
要保证全局唯一 ,我们可以采用分布式或者本地生成的 ID,使用分布式话需要有一个发号器,每次请求都要先请求一下发号器,会有一次网络调用的开销,所以 SkyWalking 最终采用了本地生成 ID 的方式,它采用了大名鼎鼎的 snowflow 算法,性能很高。
不过 snowflake 算法有一个众所周知的问题:时间回拨,这个问题可能会导致生成的 id 重复。那么 SkyWalking 是如何解决时间回拨问题的呢?
每生成一个 id,都会记录一下生成 id 的时间(lastTimestamp),如果发现当前时间比上一次生成 id 的时间(lastTimestamp)还小,那说明发生了时间回拨,此时会生成一个随机数来作为 traceId。
2.6 请求量这么多,全部采集会不会影响性能?
如果对每个请求调用都采集,那毫无疑问数据量会非常大,但反过来想一下,是否真的有必要对每个请求都采集呢,其实没有必要,我们可以设置采样频率,只采样部分数据,SkyWalking 默认设置了 3 秒采样 3 次,其余请求不采样,如图示:
这样的采样频率其实足够我们分析组件的性能了,按 3 秒采样 3 次这样的频率来采样数据会有啥问题呢。理想情况下,每个服务调用都在同一个时间点(如下图示)这样的话每次都在同一时间点采样确实没问题。
但在生产上,每次服务调用基本不可能都在同一时间点调用,因为期间有网络调用延时等,实际调用情况很可能是下图这样:
这样的话就会导致某些调用在服务 A 上被采样了,在服务 B,C 上不被采样,也就没法分析调用链的性能,那么 SkyWalking 是如何解决的呢。
它是这样解决的:如果上游有携带 Context 过来(说明上游采样了),则下游强制采集数据。这样可以保证链路完整。
2.7 skywalking的各模块组件视图
Skywalking已经支持从6个可视化维度剖析分布式系统的运行情况。
- 总览视图(Global view)是应用和组件的全局视图,其中包括组件和应用数量,应用的告警波动,慢服务列表以及应用吞吐量;
- 拓扑图(topology view)从应用依赖关系出发,展现整个应用的拓扑关系;
- 应用视图则是从单个应用的角度,展现应用的上下游关系,TopN的服务和服务器,JVM的相关信息以及对应的主机信息。
- 服务视图关注单个服务入口的运行情况以及此服务的上下游依赖关系,依赖度,帮助用户针对单个服务的优化和监控;
- 调用链(trace)展现了调用的单次请求经过的所有埋点以及每个埋点的执行时长;
- 告警视图(alarm)根据配置阈值针对应用、服务器、服务进行实时告警。
2.8 部署使用
具体参考即可:https://blog.csdn.net/weixin_42906244/article/details/125638730
参考文献:
https://blog.csdn.net/A123638/article/details/123117142
https://blog.csdn.net/weixin_38004638/article/details/115975798
https://blog.csdn.net/weixin_39866487/article/details/111581322
https://blog.csdn.net/weixin_39595621/article/details/111574769