一. 什么是链路追踪技术
包含了数据埋点、收集、存储、分析等。
以大规模分布式电商系统为例,下单行为涉及6 个系统(集群形式部署),最长链路3层: A-> C -> E 或 A -> C -> F。
解决:记录请求中各模块调用信息;通过日志增量(所有机器上)收集到集群处理,同一个请求日志串联可视化
收集方式:埋点,在 RPC 注入代码,收集调用信息。
Dapper构建调用树Tracer,traceId 标识调用链。
span两个重要信息 :span id(当前) 和 span parent ID (上个调用模块 span id ),通过这两个定位span 位置
功能:
服务耗时、瓶颈分析 :分析每个服务耗时,优化。
可视化错误:便于排查和定位,提供 ZipKin 页面支持。
链路优化: 调用频繁优化。
调用链路梳理:可视化界面,整体清晰
设计时考虑问题:
低损耗、高性能: 不影响线上服务性能。
应用透明: 业务开发不知道
可扩展性:业务增大、集群增多,监控系统都应该能把控快速变化
数据采样设计:如每条日志都记,高并发有损耗。收集太少对统计结果有影响,合理设计采样比例
二. SOFARPC 链路追踪设计原理
依赖于所集成 SOFATracer实现。SOFARPC + SOFATracer + zipKin(支持数据上报 zipkin) 完整链路追踪。SOFARPC 具备微内核设计和拓展性,不破坏开闭原则集合 SOFATracer。
SOFARPC 链路追踪设计思路:
(1)可插拔设计:SOFARPC 采用了微内核设计,易扩展
(2)总线设计。无侵入扩展方式。
(3)调用 trace 和 span
(4)数据采样设计
(5)异步刷新机制
(6)耗时计算:链路调用的耗时统计等信息获取。
(7)埋点数据透传,各模块之间的链路调用数据的透传机制。
(8)异步线程的链路调用。在异步多线程环境下如何保证 traceId 和 spanId 的有序性。
(9)链路调用日志数据的文件存储结构
2.1 可插拔设计
自己实现SPI机制, 去加载其他模块、过滤器、协议等,灵活拓展。SOFARPC 为集成 SOFATracer 采用了这套机制,可插拔。
1.实现了 Module 接口
2.方便SOFARPC 在启动时将相关模块加载进来
3.SofaTracerModule作为SOFA-PRC 链路追踪的入口,该模块有两个作用:加载sofaTracer; 订阅事件(加载时完成)
4.监听 SOFARPC这 9 种事件,完成埋点数据获取和异步磁盘写入操作
5.通过事件总线设计来订阅事件,事件发生时通知对应的订阅者做相应的操作
3.2 事件总线设计
强扩展性设计,EventBus 类似计算机数据总线,传输数据。 发布-订阅,设置多个事件点发生时写入 EventBus,订阅感兴趣事件并处理。
EventBus一旦发布新事件就通知感兴趣订阅者,SOFA-Tracer采用 SofaTracerSubscriber 订阅和处理这 9 种事件,最终链路追踪数据的获取操作都交给了 RpcSofaTracer 处理。集合 SOFATracer无侵入
3.3 调用链 Trace 和 Span
Trace :完整跟踪,树状调用链。
Span : RPC 调用过程
ClientSpan 客户端发送请求给服务端,服务端响应结果过程。
ServerSpan 是服务端收到客户端时间 到 发送响应结果给客户端的这段过程。
一个 span 包含Annotation: cs (client send);cr (client recv);sr (server recv);ss (server send)
client span 和 server span的 同一spanId(一个 RPC 调用)。 时间维度解释两者关系:
3.3.1 TraceId 生成规则
服务器 IP + 产生 ID 时间 + 自增序列 + 当前进程号
自增序列:从 1000 涨到 9000,到达 9000 后回到 1000 再开始往上涨。
3.3.2 SpanId 生成规则
3.4 数据采样设计
在 Dapper 论文中强调了数据采样的重要性,如果将每条埋点数据都刷新到磁盘上会增大链路追踪框架对原有业务性能的影响。如果采样率太低,可能会导致一些重要数据的丢失。 论文中提到如果在高并发情况下 1/1024 的采样率是足够的,也不必担心重要事件数据的丢失。因为在高并发环境下,一个异常数据出现一次,那么就会出现 1000 次。 然而在并发量不是很多的系统,并且对数据各位敏感时需要让业务开发人员手动设置采样率。
自适应采样率。 提供了 RingBuffer数据结构,设置一个 1024 的序列化槽位用于存储每个链路调用的埋点数据。 可以看做是一个圆形环状结构,RingBuffer 中的数据 从槽位 0 开始存储,一直存储到1023。 当操作1023 时会从头开始存放,放在原来槽位的数据将被覆盖。
从上图所示,当数据写入的速度远远大于 3个 consumer 的处理速度,那么环上的数据在未被处理时就被覆盖。 通过覆盖的方式来自动调整采样率,并发性越高、写入速度越快时,采样率就越低。当并发性越低、写入速度也随之变慢,则采样率就变高。
在 SOFATracer 中默认开启三个线程去负责这些数据的持久化。假设每个线程的处理速度是 x/s(条/秒), 并发写入的速度是 y/s,那么 SOFATracer 的自动化采样率为 3x / y (前提是 y >= 3x, 否则就是 100% 采样率)。
3.5 异步刷新机制
埋点数据的本地化存储涉及到磁盘操作, 磁盘 IO 速度较慢,如果在高并发环境下同步刷新磁盘给原业务带来的性能损耗是非常可观的。 链路追踪系统在数据埋点的时候应尽可能的降低系统损耗,对原业务在逻辑和性能上做到无侵入性。
SOFATracer 采用了异步刷新机制,将 RingBuffer 的数据异步刷新到磁盘。 默认情况下 会启动 3个处理线程去处理 RingBuffer 的数据,将数据异步刷新到磁盘进行持久化。
当 RingBuffer 写入新数据时就会唤醒处理线程, 并将当前存入数据的槽位设置为可用槽位。 处理线程从睡眠点醒来后,便从原来处理位置往下获取数据并处理,直到所处理数据槽位大于可用槽位,则阻塞等待。
3.6 耗时计算
耗时计算不是为了集成 SOFATracer 而单独设置的,SOFARPC 框架自身带有耗时计算的逻辑,这些时间可以用于判断 RPC 调用是否超时等。因此加入链路追踪埋点时,不需要在扩展模块中计算耗时时间,SOFARPC中已经将调用的时间耗时等信息放在 RpcInternalContext 上下文中。 在计算 RPC 调用耗时时,对原有框架性能不影响,直接去上下文获取即可。
3.7 埋点数据透传
各模块部署后独立收集埋点日志,这些调用链日志通过 traceId 串联在一起。 在 SOFARPC中,下一个模块的 spanId 的创建依赖于上一个模块的 spanId。 因此这些埋点数据如 traceId 以及 spanId 需要透传给下游模块。
数据传输一般有两种:
1. 带内透传,即在原来的 rpc 调用请求网络宽带中加入埋点数据透传给下游;
2. 带外传播,通过单独提供一个宽带来传播,不影响原调用数据和网络。
Dapper 采用带外传播,这种方式可以不影响原有业务性能。带内透传数据意味着需要增加原来网络调用的负载。SOFARPC 采用的是带内透传,直接在原来的 RPCRequest 的扩展字段中加入埋点数据,直接透传给下游。SOFARPC 的 spanId 长度相对较短,所需传递的数据相对较小,从整体上看对原业务性能影响较小。
3.8 异步线程的链路调动
在多线程并发调用环境下的数据链路埋点也是一个值得关注问题,当一个服务考虑性能问题可能会起多个线程同时调用其他不同的模块。链路系统如何保证这些调用还是符合 openTrace 规范,保证 traceId 和 spanId 有序。
一个链路调用在模块 A 是一个线程,链路调用的上下文信息如 traceId、spanId 等都是存放在 ThreadLocal。 按上图思路新起的线程 1、2、3 无法获取主线程的 ThreadLocal 数据,即无法获取调用链路数据。那么在无法获取链路调用的上线文数据时进行模块 B、C、D 的调用操作会导致收集得到的埋点数据是乱序的脏数据。
为了避免启动新线程把 链路调用的上下文 信息丢失,SOFATracer 提供了SofaTracerCallable 类,只要使用该类来实现线程逻辑,SOFATracer 会自动将链路调用的上下文信息透传给 SofaTracerCallable,因此可以像单线程一样进行调用埋点。SOFATracer 将上下文中的一些字段设置为线程安全,同样保证了多线程环境下的数据安全问题。 因此建议在多线程环境下进行一步调用时尽可能考虑使用 SofaTracerCallable, 否则调用链数据与预期有些出路。
3.9 文件存储结构
SOFA 整体开源框架对日志做了很好地分类,将不同类型的日志存放在不同的文件夹下。一方面便于收集特定日志,如埋点数据;另一方面也便于查找问题方便,日志结构和内容清晰。
在 SOFARPC 的链路追踪技术中,埋点数据的存储也采用日志文件方式进行持久化存储。tracer 日志文件包含以下文件:
文件功能
rpc-client-digest.log记录client rpc 调用的链路调用数据
rpc-client-stat.log记录 client rpc 链路调用的统计数据
rpc-server-digest.log记录 server rpc 调用的链路调用数据
rpc-server-stat.log记录 server rpc 链路调用的统计数据
static-info.log统计信息日志
tracer-self.logtracer 自身的日志记录
总结
依靠集成 SOFATrace 实现链路追踪,SOFARPC 负责数据埋点。依赖微内核和事件总线设计,不破坏开闭原则。
特点:
(1)零侵入(业务代码)追踪埋点
(2)日志数据异步刷新,不影响正常业务性能。
(3)自适应采样设计,平衡数据采集和性能问题。
(4)上报 zipkin, 与 zipkin 结合快速构建完整连续追踪系统。
(5)解决异步线程链路调用数据问题。
(6)采用 OpenTracing 规范,和其他链路追踪手机,展示的技术框架快速整合。
参考资料
[1] 《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》
[2] 全链路稳定性背后的数字化支撑:阿里巴巴鹰眼技术解密