当前位置: 首页 > news >正文

【全链路追踪】XXL-JOB添加TraceID

文章目录

  • 一、背景
    • 调用路径
    • 部署环境
    • 问题
  • 二、方案
  • 三、Demo示例
    • 1、MDC
    • 2、RequestInterceptor
    • 3、HandlerInterceptor
    • 4、logback.xml
  • 四、后续改进思路

一、背景

首先这个项目属于小型项目,由于人手以及时间限制,并未引入Skywalking等中间件来做调用链路追踪。Skywalking不在此次的讨论范围中。

其次介绍一下项目的相关背景

调用路径

项目中主要有两种调用路径

  1. Web请求走统一的网关入口,调用后端服务
  2. XXL-JOB定时任务执行调度

部署环境

Kubernetes

问题

走统一网关入口的请求不用担心,在网关那边加了TraceID,但是XXL-JOB由于是自动注册,且部署环境是在K8S内,XXL-JOB获取到的是Pod的IP,网关并未拦截到。
由于项目的逻辑较为复杂,XXL-JOB的调度任务属于其中比较重要的一块,对于前期开发的调试以及后期问题的确认,加上TraceID是非常有必要的。

二、方案

首先确认是的XXLJOB执行定时任务时,JobHandler没有TraceID,不考虑使用中间件的话,就只有两种方案了。

一种是改造XXL-JOB源码,在发起请求中添加TraceID;另一种则是在后端服务拦截到XXL-JOB的请求,在入口添加TraceID。

XXL-JOB的源码没有具体研究过,之前只是做过适配Oracle,改造起来有一定难度,所以最后采用的方案还是在后端服务拦截请求,添加TraceID。

在网上搜索了一下相关资料,发现实现起来还是比较简单的,一般都是通过spring aop的方式,在Slf4j的MDC中添加TraceID。
在这里简单介绍下MDC,之前我也没做过更多了解。

MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。某些应用程序采用多线程的方式来处理多个用户的请求。

MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问当需要记录日志时,只需要从 MDC 中获取所需的信息即可。

其实就是使用ThreadLocal来存储,而由于请求到Java后端服务时,Tomcat会分配一个线程,直至请求结束,这样就会保证我们在入口添加的TraceID,会传递到整条链路。
但是使用MDC调用存在两个问题:

  1. 子线程中日志TraceID丢失

  2. 跨服务调用日志TraceID丢失

同时项目中使用了Openfeign,在发起端使用 RequestInterceptor 来拦截,添加TraceID,然后在接收端使用 HandlerInterceptor 拦截。

即最终方案是 MDC+RequestInterceptor+HandlerInterceptor

整体的调用链路如下:

暂时无法在飞书文档外展示此内容

三、Demo示例

1、MDC

@Aspect
@Component
@Slf4j
public class XxlJobAopConfig {@Before("@annotation(com.xxl.job.core.handler.annotation.XxlJob)")public void beforeMethod() {MDC.put('traceId',UUID.randomUUID().toString().toLowerCase());}
}

2、RequestInterceptor

@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {template.header('traceId', MDC.get(HeaderExtraInfoConstants.traceId));}
}
@FeignClient(name = "test", url = "xxx", configuration = FeignRequestInterceptor.class)

3、HandlerInterceptor

@Slf4j
@Component
public class HeaderInterceptor implements HandlerInterceptor {@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception arg3) {MDC.remove('traceId');}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView arg3) {}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String traceId = request.getHeader(HeaderExtraInfoConstants.traceId);if (StringUtils.isEmpty(traceId)) {MDC.put('traceId', UUID.randomUUID().toString().toLowerCase());} else {MDC.put('traceId', traceId);}return true;}}
@Configuration
public class InterceptorConfiguration extends WebMvcConfigurationSupport {@Overrideprotected void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new HeaderInterceptor()).addPathPatterns("/**");}
}

4、logback.xml

%d{yyyy-MM-dd HH:mm:ss.SSS} ---> [%X{traceId}] ---> [%thread] ---> %-5level %logger{50} - %msg%n

四、后续改进思路

上述方案有较大的局限性,只适用于服务间通过feign调用的方式,如果有其他如okhttp的方式,需要再添加拦截器。对于多线程的问题也并未解决,常见的方式是通过重写线程池来解决。

  1. 丰富调用场景,添加拦截器

  2. 重写线程池

  3. 由于部署在K8S集群,可启用Istio进行服务治理

http://www.lryc.cn/news/134773.html

相关文章:

  • [Unity]Lua本地时间、倒计时和正计时。
  • 探究HTTP API接口测试:工具、方法与自动化
  • CSS中如何实现文字溢出省略号(text-overflow: ellipsis)效果?
  • CSDN编程题-每日一练(2023-08-21)
  • 面试题-React(四):React中的事件绑定如何实现?有几种方式?
  • Docker容器:docker镜像的创建及dockerfile案例
  • Java虚拟机(JVM):引用计数算法
  • 【AGC】Publishing api怎么上传绿色认证审核材料
  • 改变住宅区空气质量,你一定要知道!
  • 【SpringCloud】Gateway使用
  • Spring之域对象共享数据
  • Redis巩固加强(帮助迅速梳理知识,同时适用初学者理解)
  • Sui生态项目|集隐私通信、移动钱包、链上朋友圈和红包功能一体的社交应用ComingChat
  • I2S/PCM board-level 约束及同步(latencyskewbitsync)
  • vue 富文本编辑器
  • 为什么说ChatGPT还不是搜索引擎的对手
  • 2308C++协程流程
  • C#实现稳定的ftp下载文件方法
  • 八股文之计算机网络
  • kotlin 比较 let apply
  • springboot跨域踩坑笔记
  • 基于STM32+FreeRTOS的四轴机械臂
  • 【C语言】三子棋游戏——超细教学
  • redux的介绍、安装、三大核心与执行流程
  • Redis 5环境搭建
  • stm32红绿灯源代码示例(附带Proteus电路图)
  • Qt与电脑管家4
  • 使用css美化gradio界面
  • Flink流批一体计算(13):PyFlink Tabel API之SQL DDL
  • java笔试手写算法面试题大全含答案