一 相关概念
在分布式服务架构下,一个 Web 请求从网关流入,有可能会调用多个服务对请求进行处理,拿到最终结果。在这个过程中每个服务之间的通信又是单独的网络请求,无论请求流经的哪个服务除了故障或者处理过慢都会对前端造成影响。
在分布式链路追踪中有两个重要的概念:跟踪(trace)和 跨度(span)。trace 是请求在分布式系统中的整个链路视图,span 则代表整个链路中不同服务内部的视图,span 组合在一起就是整个 trace 的视图。
- traceId:用于标识某一次具体的请求ID。当用户的请求进入系统后,会在RPC调用网络的第一层生成一个全局唯一的traceId,并且会随着每一层的RPC调用,不断往后传递,这样的话通过traceId就可以把一次用户请求在系统中调用的路径串联起来。
- spanId:用于标识某一次RPC调用在分布式请求中的位置。请求到达每个服务后,服务都会为请求生成spanId。
- parent-spanId:用于标识上游RPC调用在分布式请求中的位置。请求到达每个服务后,随请求一起从上游传过来的上游服务的 spanId 会被记录成 parent-spanId,或者叫 pspanId。当前服务生成的 spanId 随着请求一起,在传到下游服务时,这个 spanId 又会被下游服务当作 parent-spanId 记录。
- MDC:(Mapped Diagnostic Context)映射诊断环境,是 log4j 和 logback 提供的一种方便在线多线程条件下记录日志的功能,可以看成是一个与当前线程绑定的 ThreadLocal。
public class MDC {
// 添加 key-value
public static void put(String key, String val) {
...}
// 根据 key 获取 value
public static String get(String key) {
...}
// 根据 key 删除映射
public static void remove(String key) {
...}
// 清空
public static void clear() {
...}
}
二 实现方式
2.1 设置TraceId
2.1.1 使用filter过滤器设置traceId
新建一个过滤器,实现Filter,重写init,doFilter,destroy方法,设置traceId放在doFilter中,在destroy中调用MDC.clear()方法。
@Slf4j
@WebFilter(filterName = "traceIdFilter",urlPatterns = "/*")
public class traceIdFilter implements Filter {
/**
* 日志跟踪标识
*/
private static final String TRACE_ID = "TRACE_ID";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
MDC.put(TRACE_ID, UUID.randomUUID().toString());
filterChain.doFilter(request, servletResponse);
}
@Override
public void destroy() {
MDC.clear();
}
}
2.1.2 使用Interceptor拦截器设置traceId
定义一个拦截器,重写preHandle方法,在方法中通过MDC设置traceId
/**
* MDC设置traceId拦截器
*
* @author china
*/
@Component
public abstract class TraceIdInterceptor