自实现分布式链路追踪 方案&实践

# 前言:排查问题是程序员的基本能力也是必须要会的,在开发环境,我们可以debug,但是一旦到了服务器上,就很难debug了,最有效的方式就是通过日志揪出bug,而一次请求的日志如果没有一个唯一的链路标识(我们下边称他为traceId),单靠程序员人工分析的话,费时费力,尤其是请求量高频的接口,更是雪上加霜,排查问题效率大打折扣,作为程序员,低效的方式是忍不了的!!!本文我将用一次实战演练,来...
摘要由CSDN通过智能技术生成

# 前言: 排查问题是程序员的基本能力也是必须要会的,在开发环境,我们可以debug,但是一旦到了服务器上,就很难debug了,最有效的方式就是通过日志揪出bug,而一次请求的日志如果没有一个唯一的链路标识(我们下边称他为traceId),单靠程序员人工分析的话,费时费力,尤其是请求量高频的接口,更是雪上加霜,排查问题效率大打折扣,作为程序员,低效的方式是忍不了的!!! 本文我将用一次实战演练,来演示常用框架/中间件/多服务 之间如何传递traceId

本文大概有如下内容: - 1. 链路追踪简述和自实现思路 - 2. 单服务内如何实现链路id的输出 - 3. 垮服务调用时,实现链路id传递的各种方式 - ( 包含http(openFeign,httpClient restTemplate)、rpc(motan、 dubbo)、mq(RocketMq)) - 4. 异步调用时,如何解决log4j2自带的ThreadLocal丢失链路id问题 - 5. 起4个服务,进行调用,观察链路追踪的效果

1、链路追踪实现简述

所谓链路追踪,就是为了 把整个请求链路从头到尾串起来,不管调用链路有多深,多复杂,只要将一次链路完整无误的串联起来,就是合格的链路追踪功能

业界不乏skywalking zipkin 等等链路追踪方面牛逼的框架,但是我们为了更轻量更灵活可控同时也是抱着学习心态,所以自己来实现链路追踪。

首先想实现链路追踪,有两点是核心,实现了这两点,问题也就不大了

  1. traceId 如何在本地 (或者说单服务内) 传递
  2. 分布式环境中,traceId如何跨服务/中间件 传递

2、单体服务 的链路追踪

首先我们先讲下单服务内的链路传递

作为java开发,最常用的就是slf4j来实现打印日志的功能(但是slf4j并不没有实现逻辑,因为 slf4j整个的定义是一个日志门面,该包中并无具体的实现,实现都是在 比如:logback log4j2等等日志实现框架中)

slf4j的门面不仅给我们提供了打印日志的功能,还提供了 org.slf4j.MDC 类, 该类的作用大概如下:

映射诊断上下文(Mapped Diagnostic Context,简称MDC)是一种工具,用于区分不同来源的交错日志输出。当服务器几乎同时处理多个客户机时,日志输出通常是交错的。 MDC是基于每个线程进行管理的

上边这个官方解释,最重要的一句话就是 MDC是基于每个线程进行管理的

上边这个太官方,说下我个人对MDC的理解:

他是一个日志的扩展,扩展的目的就是给 每个线程 输出的日志打上一个标记(一个线程只有一个标记且不能重复一般使用uuid即可),这样我们在查看日志时候,就可以根据这个标记来区分调用链路

ps: 当然了, 光往MDC中设置当前线程的链路id也是不行的你还得在log4j2.xml文件中,设置占位符,这样最终输出的日志才会带链路信息。如何设置会在 2.1节 有讲。

从代码层面看下 MDC做了啥:

  1. MDC类中通过一个 MDCAdapter实例
  2. 调用MDCAdapter的put get remove clear等方法。
  3. 而put get remove clear 具体的实现是不同厂商来做的 比如我常用的log4j2包中就实现了 MDCAdapter 接口,实现在 org.apache.logging.slf4j.Log4jMDCAdapter类中
  4. Log4jMDCAdapter类中使用了一个 ThreadContext来执行put get remove clear逻辑,
  5. 而ThreadContext 中又是一个 ThreadContextMap ThreadContextMap是一个接口 ,有不同的实现
  6. 其中默认的是 DefaultThreadContextMap 该类中维护了一个 ThreadLocal> localMap 类型的成员变量 其中map中的k,v 就是你调用MDC.put(k,v); 时传入的k v
  7. 最终你调用MDC.put(k,v);时候传入的k和v会被放倒 localMap这个ThreadLocal中去。
  8. 在你给某个线程设置了key,value后 ,log4j2在打印日志时候,将会去log4j2.xml文件中找 占位符等于 key的,然后用value把占位符替换,从而打印输出了value(也就是traceId),至此实现了单服务内的链路追踪。

把代码流程梳理下大概如下: ``` MDC(类) MDCAdapter(接口) Log4jMDCAdapter(实现类之一) ThreadContext(类) ThreadContextMap(接口) DefaultThreadContextMap //(MDC的几个操作都是往这个里边存取数据) ThreadLocal> localMap;

```

2.1、如何使用MDC来输出(单服务内的)链路信息?

  1. 往MDC中设置key value
    • image.png
  2. 在log4j2.xml 中设置占位符(注意:占位符名称要和MDC中的key一致)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值