分布式追踪:SpringBoot 3.x + MDC
如果不想引入第三方组件,比如:Sping Cloud Sleuth ,可以采用轻量级的解决方案来完成分布式系统下的日志链路追踪。
同时本文主要聚焦在日志的链路追踪,服务之间的调用链路追踪,可采用工具实现,比如:Jaeger、Zipkin、SkyWalking、Pinpoint、Elastic APM 等。
问题
Spring 默认的日志框架 Logback 中提供的 LogbackMDCAdapter 内部使用的是ThreadLocal,只有本线程才有效,子线程和下游的服务 MDC 里的值会丢失。
主要的难点是解决值传递问题,主要包括以下几个部分:
API Gateway 网关中如何传递 MDC 中的 TraceId
微服务之间互相远程调用时如何传递 MDC 中的 TraceId
异步情况下(线程池)如何传递 MDC 中的 TraceId 到子线程
........
环境准备
- Java 21
- Spring Boot 3.2.4
- Spring Cloud 2023.0.1
- Spring Cloud Gateway 4.1.2
- Spring Cloud OpenFeign 4.1.1
- Alibaba Transmittable Thread Local 2.14.5
如何搭建微服务框架代码不再这里赘述。
01 API Gateway 网关
创建一个GlobalTraceFilter
进行 Web 请求 和 路由请求的 TraceId 过滤处理,参考:
配置 Logback 文件 logback-spring.xml:
<springProperty scope="context" name="appName" source="spring.application.name"/>
<property name="CONSOLE_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%level){blue} %clr(${PID}){magenta} %clr([%X{traceId:-}]){yellow} %clr([${appName}]){blue} %clr([%thread]){orange} %clr(%-40.40logger{39}){cyan} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
02 下游微服务拦截 Http Header
解析 Http 请求 Header 中的 TraceId,可以看到由第一步 Api Gateway 设置的。创建一个
03 下游微服务互相调用
创建 OpenFeign Interceptor ,下游服务之间的相互调用统一使用 FeignClient 进行调用。
04 异步多线程传递问题
改造 LogbackMDCAdapter
新建一个 MDCAdapter 使用阿里巴巴的 TransmittableThreadLocal 替换 LogbackMDCAdapter中 ThreadLocal,来解决多线程无法传递 MDC 值问题。
SpringBoot 3.0 以上版本后,无法替换 Logback 的 MDC Adapter,需要将我们新建的 MDC Adapter配置到spi文件,参考下图 META-INFO下services 目录中的SLF4JServiceProvider。
成功后日志输出如下:
但是每次启动都会有类似的错误提醒,后续看下是否可以优化。
替换 Spring 默认线程池
同理也是使用 alibaba 的 TtlRunnable进行替换。
小结
随着系统管理的服务数量的不断增加,了解系统在生产环境的状态愈发困难,同时也导致排查问题故障变得更加困难,建议在考虑采用微服务架构之前,将日志聚合和分布式追踪作为先决条件。
附录
Gateway 改造 MDC 后验证:
下游服务调用:
1)通过 Gateway 请求下游服务
2)下游服务日志查看 TraceId
可观测性(Elastic Stack ):
关于如何部署可视化工具,参考公众号之前 Elastic 可观测性系列文章。
Spring Cloud Sleuth: