MDC是什么鬼?用法、源码一锅端

近期用到阿里的一款开源的数据同步工具 Canal,不经意之中看到了 MDC 的用法,而且平时项目中也多次用到 MDC,乘隙科普一把。

通过今天的分享,能让你轻松 get 如下几点,绝对收获满满。

a)MDC 快速入门;

b)MDC 源码解读;

c)MDC 醒目什么?

阿里开源项目 Canal:

MDC是什么鬼?用法、源码一锅端

老项目这么用过:

MDC是什么鬼?用法、源码一锅端

然则无论怎么用,都逃不过 MDC API 的使用,下面先花一分钟快速入门,然后再逐步去深入 MDC。

1. MDC 快速入门

MDC 全称是 Mapped Diagnostic Context,可以大略的明白成是一个线程平安的存放诊断日志的容器。

首先看看 MDC 基本的 API 的用法,能抛代码的就不多空话(凭据 logback 官方案例改编)。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;

/**
 * MDC快速入门示例
 *
 * @author 一猿小讲
 */
public class SimpleMDC {

    private static final Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
    public static final String REQ_ID = "REQ_ID";

    public static void main(String[] args) {
        MDC.put(REQ_ID, UUID.randomUUID().toString());
        logger.info("最先挪用服务A,举行营业处置");
        logger.info("营业处置完毕,可以释放空间了,制止内存泄露");
        MDC.remove(REQ_ID);
        logger.info("REQ_ID 另有吗?{}", MDC.get(REQ_ID) != null);
    }
}

代码编写完,貌似只有 MDC.put(K,V) 、MDC.remove(K) 两句是生疏的,先不着急注释它,等案例跑完就懂了,咱们继续往下看。

接下来设置 logback.xml,通过 %X{REQ_ID} 来打印 REQ_ID 的信息,logback.xml 文件内容如下。

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>[%t] [%X{REQ_ID}] - %m%n</Pattern>
        </layout>
    </appender>
    <root level="debug">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

引入依赖包,让程序快点跑起来看看效果。

<dependencies>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.7</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
</dependencies>

程序跑起来,输出截图如下。

MDC是什么鬼?用法、源码一锅端

凭据输出效果剖析,能够获得两条结论。

第一:如图中红色圈住部门所示,当 logback 内置的日志字段不能知足营业需求时,便可以借助 MDC 机制,将营业上想要输出的信息,通过 logback 给打印出来;

第二:如蓝色圈住部门所示,当挪用 MDC.remove(Key) 后,便可将营业字段从 MDC 中删除,日志中就不再打印请求 ID 啦;

一气呵成,我们迅速看看在多线程情况下,使用 MDC 会发生什么征象呢?

照样基于上面的代码,把代码段放到了线程体内,稍微举行革新了一下,代码如下。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;

/**
 * MDC快速入门示例
 *
 * @author 一猿小讲
 */
public class SimpleMDC {
    public static void main(String[] args) {
        new BizHandle("F0000").start();
        new BizHandle("F9999").start();
    }
}

class BizHandle extends Thread {

    private static final Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
    public static final String REQ_ID = "REQ_ID";

    private String funCode;

    public BizHandle(String funCode) {
        this.funCode = funCode;
    }

    @Override
    public void run() {
        MDC.put(REQ_ID, UUID.randomUUID().toString());
        logger.info("最先挪用服务{},举行营业处置", funCode);
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            logger.info(e.getMessage());
        }
        logger.info("服务{}处置完毕,可以释放空间了,制止内存泄露", funCode);
        MDC.remove(REQ_ID);
    }
}

程序跑起来看看效果。

MDC是什么鬼?用法、源码一锅端

依据程序输出举行剖析,能够看到线程 Thread-0 与 Thread-1 在 MDC 中放入的 REQ_ID 的值是互不影响,也就是说 MDC 中的值是与线程绑定在一起的。

好了,入门程序就这么简朴,简朴做个小结。

a)MDC 提供的 put 方式,可以将一个 K-V 的键值对放到容器中,并且能保证同一个线程内,Key 是唯一的,差别的线程 MDC 的值互不影响;

b)  在 logback.xml 中,在 layout 中可以通过声明 %X{REQ_ID} 来输出 MDC 中 REQ_ID 的信息;

c)MDC 提供的 remove 方式,可以消灭 MDC 中指定 key 对应的键值对信息。

1个工具,助你提升K8S故障排查效率!

通过快速入门的程序,得知 MDC 的值与线程是绑定在一起的,差别线程互不影响,MDC 背后到底是怎么实现的呢?不妨从源码上看一看。

2. MDC 源码解读

解读源码之前,要提提 SLF4J,全称是 Simple Logging Facade for Java,翻译过来就是「一套简朴的日志门面」。是为了让研发职员在项目中切换日志组件的利便,特意抽象出的一层。

项目开发中经常这么界说日志工具:

Logger logger = LoggerFactory.getLogger(SimpleMDC.class)

其中 Logger 就来自于 SLF4J 的规范包,项目中一旦这样界说 Logger,在底层就可以无缝切换 logback、log4j 等日志组件啦,这或许就是 Java 为什么要提倡要面向接口编程的利益。

见证事业的时刻要到了,下面就好好揭秘一下 MDC 背后藏着什么东东?

首先通过 org.slf4j.MDC 的源码,可以很清晰的知道 MDC 主要是通过 MDCAdapter 来完成 put、get、remove 等操作。

MDC是什么鬼?用法、源码一锅端

不出所料 MDCAdapter 也是个接口。在 Java 的天下里,应该都知道界说接口的目的:就是为了界说规范,让子类去实现。

MDCAdapter 就和 JDBC 的规范类似,专门用于界说操作规范。JDBC 是为了界说数据库操作规范,让数据库厂商(MySQL、DB2、Oracle 等)去实现;而 MDCAdapter 则是让详细的日志组件(logback、log4j等)去实现。

MDC是什么鬼?用法、源码一锅端

MDCAdapter 接口的实现类,有 NOPMDCAdapter、BasicMDCAdapter、LogbackMDCAdapter 以及 Log4jMDCAdapter 等等几种,其中 log4j 使用的是 Log4jMDCAdapter,而 Logback 使用的是 LogbackMDCAdapter。

MDC是什么鬼?用法、源码一锅端

本次重点说 LogbackMDCAdapter 的源码,截图如下。

MDC是什么鬼?用法、源码一锅端

通过图中标注 1、2 的代码,可以清晰的知道 MDC 底层最终使用的是 ThreadLocal 来举行的实现(水落石出,花落它家)。

a)ThreadLocal 许多地方叫做线程内陆变量,也有些地方叫做线程内陆存储。

b)ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,削减同一个线程内多个函数或者组件之间一些公共变量的通报的复杂度。 

c)ThreadLocal 使用场景为用来解决数据库毗邻、Session 治理等。 

本次纰谬 ThreadLocal 展开去说,若感兴趣的可自行填补一下。

3. MDC 醒目什么?

MDC 的应用场景实在蛮多的,下面简朴枚举几个。

a)在 WEB 应用中,若是想在日志中输出请求用户 IP 地址、请求 URL、统计耗时等等,MDC 基本都能支持;

b)在 WEB 应用中,若是能画出用户的请求到响应整个历程,势必会快速定位生产问题,那么借助 MDC 来保留用户请求时发生的 reqId,当请求完成后,再将这个 reqId 举行移除,这么一来通过 grep reqId 就能轻松 get 整个请求流程的日志轨迹;

c)在微服务盛行的当下,链路跟踪是个难题,而借助 MDC 去埋点,巧妙实现链路跟踪应该不是问题。

4. 写在最后

行文至此,靠近尾声,本次主要让人人对 MDC 举行快速入门,并通过剖析源码,窥探 MDC 的背后,最终分享了一些 MDC 在项目研发中能做什么的实践思绪,迎接人人多去实验实现。

另外,若是急需分布式挪用链路跟踪、监控的轮子,在自研的轮子已经跟不上项目的生长时,有以下几款开源的轮子推荐,不妨拿去一试。

MDC是什么鬼?用法、源码一锅端

 

 一起聊手艺、谈营业、喷架构,少走弯路,不踩大坑,迎接继续关注「一猿小讲」,会连续输出更多原创精彩分享!

可以微信搜索民众号「 一猿小讲 」回复「1024」get 经心为你准备的编程进阶资料。

原创文章,作者:28x29新闻网,如若转载,请注明出处:https://www.28x29.com/archives/5343.html