MDC
背景: 线上排查问题时,请求在多个微服务之间进行调用,并发量较大的情况下,想跟踪某一个请求的链路,是需要花费一些时间才能梳理出来,而且还依赖于你的业务字段。而我们需要的是快速定位,快速纠错,因此就需要一个唯一的全局标识用来识别并跟踪这个请求。
想法: 一个唯一全局标识,并且还能在上下游之间进行传递,那可以对这个请求 request 做些特殊操作,我们可以在请求头上添加这个全局标识,比如使用UUID,在进入每一个微服务之前进行拦截 (filter/intercept),获取这个UUID并设置到当前线程中。输出日志时,就把这个UUID也输出到日志中,这样便可以快速识别并定位。如果在业务代码中使用了多线程,也应该记录,这就需要使用到本次介绍的重点内容:MDC
介绍: MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 中包含的内容可以被同一线程中执行的代码所访问,当前线程的子线程也会继承其父线程中的 MDC 的内容。
我们日志用的是 slf4j + logback,看看 slf4j-api 包结构,发现了一个 spi ,是不是很熟悉,虽然这个 spi 只是一个包名,但是实质是一样的

在 slf4j-api 包下有一个 MDCAdapter接口,如下
public interface MDCAdapter {
//通过k-v设置
public void put(String key, String val);
public String get(String key);
public void remove(String key);
public void clear();
public Map getCopyOfContextMap();
public void setContextMap(Map contextMap);
}
由于使用的是logback日志,对应的logback也提供了MDCAdapter接口的实现,如下

我们看看logback里面是如何实现 MDCAdapter 接口的。发现是基于InheritableThreadLocal 来实现的,这个就很好的处理的父子线程的问题,也就是可以trace线程池
public final class LogbackMDCAdapter implements MDCAdapter {
final InheritableThreadLocal<Map<String, String>> copyOnInheritThreadLocal = new InheritableThreadLocal<Map<String, String>>();
private static final int WRITE_OPERATION = 1;
private static final int READ_OPERATION = 2;
// keeps track of the last operation performed
final ThreadLocal<Integer> lastOperation = new ThreadLocal

本文介绍了在高并发场景下,如何使用MDC(MappedDiagnosticContext)作为全局唯一标识跟踪请求链路,通过在请求头添加UUID并在日志中记录,实现实时定位问题。同时,文章详细展示了MDC在logback中的实现方式,以及如何配合日志收集工具如ELK或LPG进行统一的日志管理。
最低0.47元/天 解锁文章
1283





