一、目标
开发排查零碎问题用得最多的伎俩就是查看系统日志,然而在分布式环境下应用日志定位问题还是比拟麻烦,须要借助 全链路追踪ID
把上下文串联起来,本文次要分享基于 Spring Boot
+ Dubbo
框架下 日志链路追踪ID
的实现计划选型思路。
目前大多数分布式追踪零碎的思维模型都来自 Google's Dapper 论文
全链路追踪的核心思想:
- 为每条申请都独自调配一个惟一的
traceId
用来标识一条申请链路,该traceId
会贯通整个申请处理过程的所有服务 - 每个服务/线程都领有本人的
spanId
标识,代表申请的其中一段解决步骤 - 一个申请蕴含一个
traceId
和一个或多个spanId
日志全链路追踪 就是在每条系统日志里都增加显示traceId
和spanId
信息
二、计划选型
2.1. 计划一(apm-toolkit)
这是 SkyWalking
的一个日志插件,通过这个插件能够在日志中输入traceId
2.1.1. 应用形式
配置依赖,在 pom 文件中增加以下内容
<dependency> <groupId>org.apache.skywalking</groupId> <artifactId>apm-toolkit-logback-1.x</artifactId> <version>8.1.0</version></dependency>
配置日志模板,批改 logback-spring.xml
文件中 Appender
元素的 encoder
为以下内容
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{35} - %msg%n</pattern> </layout></encoder>
ps: pattern 中的内容按需批改,其中的 %tid 就是相当于 traceId,默认 TID:N/A,当有申请调用时会生成并显示 traceId
2.1.2. 总结
- 长处:无需编码,业务无入侵,可与
SkyWalking
的图形化界面中应用该ID疾速定位各种接口的调用关系。 毛病:强耦合
SkyWalking
能力失效- 必须增加sk的
javaagent
- 必须部署
SkyWalking
服务端
- 必须增加sk的
2.2. 计划二(sleuth)
Sleuth
是 Spring Cloud
的组件之一,它为 Spring Cloud
实现了一种分布式追踪解决方案,兼容Zipkin,HTrace与其余日志追踪零碎
2.2.1. 应用形式
配置父依赖,在 pom 文件中增加以下内容治理版本号
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth</artifactId> <version>2.2.4.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency></dependencyManagement>
配置依赖,在 pom 文件中增加以下内容
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId></dependency>
适配dubbo,要让 sleuth
反对 dubbo
框架,须要减少以下两个步骤:
首先增加 dubbo 的插件依赖
<dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-dubbo-rpc</artifactId> <version>5.12.6</version></dependency>
配置 dubbo 过滤器
dubbo: provider: filter: tracing consumer: filter: tracing
配置日志模板,批改 logback-spring.xml
文件中 Appender
元素的 encoder
为以下内容
<encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{X-B3-TraceId},%X{X-B3-SpanId}] [%thread] %-5level %logger{35} - %msg%n</pattern> <charset>utf-8</charset></encoder>
ps: pattern 中的内容按需批改,其中的 %X{X-B3-TraceId} 为 traceId,%X{X-B3-SpanId} 为 spanId
2.2.2. 总结
- 长处:业务无入侵,有丰盛的插件进行扩大包含定时工作、MQ等。
- 毛病:
brave-instrumentation-dubbo-rpc
不反对dubbo 2.7.x
须要自行开发插件。
2.3. 计划三(自研)
2.3.1. 无入侵减少 traceId
应用 Logback
的 MDC
机制,在日志模板中退出 traceId
标识,取值形式为 %X{traceId}
- 零碎入口(api网关)创立
traceId
的值 - 应用
MDC
保留traceId
- 批改
logback
配置文件模板格局增加标识%X{traceId}
MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种不便在多线程条件下记录日志的性能。
2.3.2. 跨线程传递
解决 traceId
跨线程失落问题
因为 MDC
外部应用的是 ThreadLocal
所以只有本线程才无效,子线程和上游的服务 MDC
里的值会失落;
须要解决 Spring
的各种线程池与异步办法的父子线程间传递。
解决思路:重写一个 MDCAdapter
应用阿里的 TransmittableThreadLocal
替换原来的 ThreadLocal
对象,解决各种线程池(ExecutorService
/ ForkJoinPool
/ TimerTask
)父子过程传值问题。
须要应用TtlRunnable
和TtlCallable
来润饰传入线程池的Runnable
和Callable
2.3.3. 跨过程传递
解决 traceId
跨过程失落问题
dubbo服务 应用 org.apache.dubbo.rpc.Filter
创立一个过滤器进行 traceId
传递
- 服务消费者:负责传递链路追踪 ID
- 服务提供者:负责接管 ID 并保留到
MDC
中
2.3.4. 总结
- 长处:业务无入侵,最小依赖,扩大灵便,适配性强。
- 毛病:须要自行实现,有大量的开发工作量。
三、计划总结
计划 | 开发工作量 | 可维护性 | 入侵性 | 性能 |
---|---|---|---|---|
apm-toolkit | 无 | 低 | 业务无入侵 | 中 |
sleuth | 中 | 中 | 业务无入侵 | 中 |
自研 | 高 | 高 | 业务无入侵 | 高 |
扫码关注有惊喜!