40张图看懂分布式追踪系统原理及实践
前言
分布式追踪系统原理及作用
SkyWalking的原理及架构设计
我司在分布式调用链上的实践
分布式追踪系统的原理及作用
接口的 RT 你怎么知道?
是否有异常响应?
主要慢在哪里?
单体架构


微服务架构


排查问题难度大,周期长
特定场景难复现
系统性能瓶颈分析较难
自动采取数据
分析数据产生完整调用链:有了请求的完整调用链,问题有很大概率可复现
数据可视化:每个组件的性能可视化,能帮助我们很好地定位系统的瓶颈,及时找出问题所在

分布式调用链标准 - OpenTracing

这样 OpenTracing 通过提供平台无关,厂商无关的 API,使得开发人员能够方便地添加追踪系统的实现。

Trace:一个完整请求链路
Span:一次调用过程(需要有开始时间和结束时间)
SpanContext:Trace 的全局上下文信息, 如里面有traceId


全局 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,这样才能把两个紧邻的调用关联起来。


怎么自动采集 span 数据:自动采集,对业务代码无侵入
如何跨进程传递 context
traceId 如何保证全局唯一
请求量这么多采集会不会影响性能
SkyWalking的原理及架构设计
怎么自动采集 span 数据

如何跨进程传递 context
dubbo 中的 attachment 就相当于 header ,所以我们把 context 放在 attachment 中,这样就解决了 context 的传递问题。
小提示:这里的传递 context 流程均是在 dubbo plugin 处理的,业务无感知,这个 plugin 是怎么实现的呢,下文会分析traceId 如何保证全局唯一
图示: snowflake 算法生成的 id
每生成一个 id,都会记录一下生成 id 的时间(lastTimestamp),如果发现当前时间比上一次生成 id 的时间(lastTimestamp)还小,那说明发生了时间回拨,此时会生成一个随机数来作为 traceId。这里可能就有同学要较真了,可能会觉得生成的这个随机数也会和已生成的全局 id 重复,是否再加一层校验会好点。请求量这么多,全部采集会不会影响性能?
这样的采样频率其实足够我们分析组件的性能了,按 3 秒采样 3 次这样的频率来采样数据会有啥问题呢。理想情况下,每个服务调用都在同一个时间点(如下图示)这样的话每次都在同一时间点采样确实没问题
但在生产上,每次服务调用基本不可能都在同一时间点调用,因为期间有网络调用延时等,实际调用情况很可能是下图这样
SkyWalking 的基础架构
首先当然是节点数据的定时采样,采样后将数据定时上报,将其存储到 ES, MySQL 等持久化层,有了数据自然而然可根据数据做可视化分析。SkyWalking 的性能如何


对多语言的支持,组件丰富:目前其支持 Java, .Net Core, PHP, NodeJS, Golang, LUA 语言,组件上也支持dubbo, mysql 等常见组件,大部分能满足我们的需求。 扩展性:对于不满足的插件,我们按照 SkyWalking 的规则手动写一个即可,新实现的插件对代码无入侵。
我司在分布式调用链上的实践
SkyWalking 在我司的应用架构

我司对 SkyWalking 作了哪些改造和实践
预发环境由于调试需要强制采样 实现更细粒度的采样? 日志中嵌入traceId 自研实现了 SkyWalking 插件
预发环境由于调试需要强制采样

实现更细粒度的采样?
我们知道这种方式默认是 3 秒采样前 3 次,其他请求都丢弃,这样的话有个问题,假设在这台机器上在 3 秒内有多个 dubbo,mysql,redis 调用,但在如果前三次都是 dubbo 调用的话,其他像 mysql, redis 等调用就采样不到了,所以我们对 skywalking 进行了改造,实现了分组采样,如下
就是说 3 秒内进行 3 次 redis, dubbo, mysql 等的采样,也就避免了此问题日志中如何嵌入traceId?

首先 log4j 的插件要定义一个类,这个类要继承 LogEventPatternConverter 这个类,并且用标准 Plugin 将其自身声明为 Plugin,通过 @ConverterKeys 这个注解指定了要替换的占位符,然后在 format 方法里将其替换掉。这样在日志中就会出现我们想要的 TraceId ,如下
我司自研了哪些 skywalking 插件

插件定义类: 指定插件的定义类,最终会根据这里的定义类打包生成 plugin Instrumentation: 指定切面,切点,要对哪个类的哪个方法进行增强 Interceptor,指定步骤 2 中要在方法的前置,后置还是异常中写增强逻辑
而 MonitorFilter 可以拦截所有客户端发出请求或者服务端处理请求,所以我们可以对 MonitorFilter 作增强,在其调用 invoke 方法前,将全局 traceId 注入到其 Invocation 的 attachment 中,这样就可以确保在请求到达真正的业务逻辑前就已经存在全局 traceId。

// skywalking-plugin.def 文件dubbo=org.apache.skywalking.apm.plugin.asf.dubbo.DubboInstrumentation
这样打包出来的插件就会对 MonitorFilter 的 invoke 方法进行增强,在 invoke 方法执行前对期 attachment 作注入全局 traceId 等操作,这一切都是静默的,对代码无侵入的。
总结
评论
