如何在SpringBoot微服务中实现用于链路追踪的req id(请求ID)
Spring Boot 微服务请求链路唯一标识透传实现方案
该需求核心是实现全链路TraceId(请求唯一标识)的透传,优先级为客户端传入标识 > 服务端自动生成标识,最终通过日志关联全链路处理信息。
1. 核心实现步骤
定义链路上下文持有类
基于ThreadLocal实现线程级别的TraceId存储,避免多请求数据混乱,用完需主动清理避免内存泄漏,示例代码如下:public class TraceContextHolder { private static final ThreadLocal<String> TRACE_ID_HOLDER = new ThreadLocal<>(); public static void setTraceId(String traceId) { TRACE_ID_HOLDER.set(traceId); } public static String getTraceId() { return TRACE_ID_HOLDER.get(); } public static void clear() { TRACE_ID_HOLDER.remove(); } }编写请求拦截器生成/获取TraceId
实现Spring的HandlerInterceptor,在请求进入时优先提取客户端传入的标识,无传入则自动生成,同时存入上下文和SLF4J的MDC(映射诊断上下文)方便日志打印,请求结束后清理上下文:@Component public class TraceInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 优先从请求头取,其次从查询参数取客户端传入的TraceId String traceId = request.getHeader("X-Trace-Id"); if (traceId == null || traceId.isBlank()) { traceId = request.getParameter("traceId"); } // 无传入则自动生成UUID作为TraceId if (traceId == null || traceId.isBlank()) { traceId = UUID.randomUUID().toString().replace("-", ""); } TraceContextHolder.setTraceId(traceId); MDC.put("traceId", traceId); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 请求结束清理上下文,避免内存泄漏 TraceContextHolder.clear(); MDC.remove("traceId"); } }之后将该拦截器注册到Spring MVC配置中即可生效。
日志格式配置
修改日志框架(如Logback)的输出格式,增加%X{traceId}占位符,所有日志就会自动携带当前请求的TraceId,示例格式配置:<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - traceId:%X{traceId} - %msg%n</pattern>跨组件/跨服务透传适配
- 若使用Feign做跨服务调用:自定义
RequestInterceptor将TraceId放入下游请求头@Component public class FeignTraceInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { String traceId = TraceContextHolder.getTraceId(); if (traceId != null) { template.header("X-Trace-Id", traceId); } } } - 若使用异步线程/
@Async注解:自定义线程池装饰器,将父线程的TraceId传递到子线程中 - 若使用消息队列:发送消息时将TraceId放入消息属性,消费时取出存入当前线程的上下文和MDC中
- 若使用Feign做跨服务调用:自定义
2. 排查使用方式
请求出错后,直接根据返回的TraceId或者用户提供的传入TraceId,检索所有服务的日志,即可拿到该请求在全链路各个节点的完整处理日志,快速定位异常节点和原因。
如果不想从零实现全链路逻辑,也可以直接使用Spring官方提供的链路追踪组件,基于上述逻辑做了封装,可自行配置透传规则满足需求。
内容的提问来源于stack exchange,提问作者Ayush Goyal
相关产品推荐
相关产品推荐

