分布式链路追踪是一种用于分析和监视应用程序的方法,特别是那些使用微服务体系结构构建的应用程序; 分布式追踪有助于查明故障发生的位置以及导致性能低下的原因,开发人员可以使用分布式跟踪来帮助调试和优化他们的代码。
一个典型的链路追踪系统如图1所示,如果想知道一个请求在哪个环节出现了问题,就要知道这个请求调用了哪些服务,调用的顺序和层级关系。这些调用信息像链条一样环环相扣,我们称之为调用链。而在这条链中,每个调用点,比如服务A对服务B的调用,服务B访问数据库,我们称之为Span。将一次请求看作一个 Trace,使用唯一的 TraceId 标识,通过 SpanId 和 ParentId 记录调用顺序和层级关系,使用 Timestamp 记录调用的各项时间指标。
把 TraceId 相同的 Span 都整合起来,就可以绘制出这个 Trace 的调用情况图,帮助分析问题。如图2所示。
借鉴Google的Dapper,选择HBase来存储Trace数据,它支持超大规模数据存储和稀疏存储动态列的特性可以满足存储Trace的需求,存储格式如下:
TraceId | Span1 | Span2 | Span3 | Span4 | ... |
135465 | <data> | null | <data> | <data> | ... |
122342 | <data> | null | <data> | null | ... |
121112 | <data> | <data> | <data> | null | ... |
... | ... | ... | ... | ... | ... |
为了避免海量的Trace数据对HBase的冲击,我们使用
Kafka在中间缓冲。Consumer节点从Kafka拉取到Span
后,以TraceId作为RowKey,SpanId作为ColumnKey存
到HBase中。这种方式受限于HBase的写入速度,Span从产生到被Consumer消费有十几分钟到几小时的延迟,导致链路追踪系统可用性较低。
解决方案
通过聚合器将同一个Trace的Span聚合起来,以顺序刷盘器顺序写磁盘的方式,优化HBase的写入性能,保证日志查询实时性。
如此大数据量,kafka是必须的
消费者Consumer从消息队列Kafka服务端拉取一条日志Span,进入聚合器处理。
聚合器从Span中提取Trace,判断Trace是否已经有缓冲区Buffer,如果有则存入缓冲区Buffer,没有则分配一个缓冲区Buffer,并记录Span的发生时间traceTime,以该Trace聚合所有Span,缓存在缓冲区Buffer。
定时Job以traceTime判断该Trace的所有Span是否结束,如果结束将当前待刷盘的所有Trace从缓冲区Buffer整理到克隆缓冲区CloneBuffer。
顺序刷盘器用JavaNIO组件FileChannel 固定分配独立的内存空间,NIO组件FileChannel 一次write调用把克隆缓冲区CloneBuffer中待刷盘的所有Trace写入磁盘。
/**
* 关于实现 用1个TxId 查一次请求 在不同服务上log 的总结
需要组件:eureka、gateway、A服务、B服务
* 1. eureka : 管理每个服务
* 2. gateway :转发A B的服务,负载均衡,并注册到eureka,引入sleuth服务
* 3. A 服务:注册到eureka,引入sleuth服务,spring.application.name , servlet.context-path都要配,A调B服务,
* 必须自定义Restemplate,并且必须加@LoadBalanced,否则不走door,各服务TxId不一样
* 4. B 服务:注册到eureka,引入sleuth服务,spring.application.name , servlet.context-path都要配
* 5. todo logback.xml 明确TxId在日志中的格式
* 6. todo accesslog 在tomcat中配,带上TxId
*/