MDC + 微服务请求链路追踪spring cloud sleuth
MDC
- mdc相信很多人都已经知道是什么东西了,我们在可以在mdc中put某些属性,比如在请求开始时生成一个唯一的requestId,登陆的用户id,登陆的设备类型等等,然后在日志文件中取出打印,来帮助我们排查问题等
//可以在filter或者intercepter中putmdc数据,也可以通过aop拦截在防治法执行前处理mdc数据
MDC.put("APPTYPE","apptype");
MDC.put("DEVICE_ID","DEVICEID");
//每次请求生成唯一的请求码,便于日志查询和分析
String requestId = System.currentTimeMillis() + "_" + RandomUtils.nextInt(1000);
MDC.put("REQUEST_ID",requestId);//请求唯一id,
-
logback.xml 文件配置,通过配置pattern,就可以取出对应的属性值,打印
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}[%X{REQUEST_ID}][%X{USER_ID}][%thread]%-5level - %logger - %msg%n</pattern>
spring cloud sleuth
上面的场景只适合在单应用的场景中,现在基本上都是微服务调用,服务和服务之间通过rpc调用,那么我们的mdc怎么跨应用传递呢
-
第一种办法,也是我们以前项目中用到的,在我们A服务代用B服务时,我们的请求参数统一继承自一个基类baseRequestObject。在这个基类中定义mdc中的属性,在服务A调用服务B时从mdc中取出对应的属性,初始化到基类baseRequestObject中,
在服务B中,通过aop拦截请求参数baseRequestObject,取出对应的属性在放入mdc中,这样我们在服务B中就可以获取到相同的mdc属性值了
优点
:可以定制化,我们想在mdc中放什么就放什么,可以在非 spring cloud 项目中使用缺点
: 过去复杂了,如果我们的服务请求链路很长,每个服务中都要去aop拦截 -
第二种办法就是spring cloud 支持的 spring cloud sleuth
- 引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
-
logback.xml日志文件配置
<Property name="pattern">%d [%thread] [%X{springAppName}][%X{X-B3-TraceId}] [%X{X-B3-SpanId}] [%X{X-B3-ParentSpanId}] [%X{X-Span-Export}] %-5level %logger{36} %line - %msg%n%ex</Property>
springAppName
:应用名X-B3-TraceId
: 每次请求的唯一IDX-B3-SpanId
: 发生的特定操作的ID,可以看做是应用级别的X-Span-Export
是否是发送给Zipkin这个就是一个可视化的请求链路追踪的应用吧,没怎么用过,后面在研究2019-06-11 10:19:16.210 INFO [server-b,3db11968703f528a,40fcc734c73c12a1,false] 12352 --- [nio-8082-exec-3] com.example.serverb.ServiceBController : start service b 2019-06-11 10:19:16.210 INFO [server-b,3db11968703f528a,40fcc734c73c12a1,false] 12352 --- [nio-8082-exec-3] com.example.serverb.ServiceBController : end service b 2019-06-11 10:19:16.210 INFO [server-b,3db11968703f528a,40fcc734c73c12a1,false] 12352 --- [nio-8082-exec-3] com.example.serverb.UserService : ---prirnt 2019-06-11 10:19:16.238 INFO [server,3db11968703f528a,b1044aac91569268,false] 516 --- [nio-8081-exec-1] com.example.server.HelloController : server end 2019-06-11 10:19:16.261 INFO [client,3db11968703f528a,3db11968703f528a,false] 3292 --- [nio-8080-exec-1] com.example.client.HelloController : end client
3db11968703f528a
就是我们的请求的唯一ID,如果我们整合了ELK日志系统的话,通过这个id去查询日志非常方便定位问题
这个就是mdc里面存放的东西,
原理其实很简单,spring cloud sleuth 把这些属性放在了请求头中,传递到下一个服务应用当中
TracingFeignClient
具体的处理逻辑在这个类中
static final Propagation.Setter<Map<String, Collection<String>>, String> SETTER = new Propagation.Setter<Map<String, Collection<String>>, String>() {
@Override
public void put(Map<String, Collection<String>> carrier, String key,
String value) {
if (!carrier.containsKey(key)) {
carrier.put(key, Collections.singletonList(value));
if (log.isTraceEnabled()) {
log.trace("Added key [" + key + "] and header value [" + value + "]");
}
}
else {
if (log.isTraceEnabled()) {
log.trace("Key [" + key + "] already there in the headers");
}
}
}
@Override
public String toString() {
return "Map::set";
}
};
优点
:简单方便,快速集成,推荐