暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

从MDC说分布式链路追踪的前世今生

互联网活化石 2021-06-26
3262

我们可能需要一个日志脱敏操作,即在日志中不直接打印用户username或phone等敏感信息,取而代之的是打印一个UUID,既能追踪用户,又能保密。


或者为了能够快速定位问题,通常需要在日志中记录请求url,请求方法,用户ID,请求ID等等等等。硬编码的形式log.info("requestUrl:{}, userId: {}......", requestUrl, userId);显然是无法满足要求的,这样实现工作量大,易出错,改动也极其不便。

这个时候,可能就需要MDC了。


1

MDC


MDC是酸辣粉日志框架(戏称,正经叫法是slf4j)提供的一种机制,全名Mapped Diagnostic Contexts ,映射诊断上下文,主要用在做日志链路跟踪时,动态配置用户自定义的一些信息,比如requestId、sessionId等等。

使用MDC只需要几行代码就能轻松应对上述需求。实现一个Filter,使用MDC.put(key, val)写入需要打印的参数。

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws
    IOException,
    ServletException {

    try {
    MDC.put("requestUri", request.getRequestURI());
    MDC.put("uuid", UUID.randomUUID().toString().replace("-", ""));
    MDC.put("ip", request.getRemoteAddr());
    User requestUser = (User) request.getAttribute("user");
    if (requestUser != null) {
    MDC.put("uid", String.valueOf(requestUser.getId()));
    }
    } catch (Exception e) {
    logger.error("init MDC error.", e);
    }
    try {
    filterChain.doFilter(request, response);
    } finally {
    MDC.clear();
    }
    }
    }


    接下来注册这个bean即可。当然,也可以用AOP的方式来向MDC里塞东西。最后,只需要配置下pattern即可,pattern里使用使用%X{} 取值。比如%X{requestUri} %X{uuid} %X{uid}。

    很容易想到,MDC能在一个请求里实现透传,用的应该是ThreadLocal。MDC应该是Java里最早使用的全链路方案,最简单,也最有效。


    2

    Dapper框架


    MDC有一个问题,它是单体的,做不到分布式的链路追踪。现代分布式链路追踪最早的实现应该起源于Google的Dapper。此后,业界有名的追踪系统,无论是国外 Twitter 的Zipkin 、Naver 的Pinpoint ,或者大众点评的CAT 、已进入Apache基金会的SkyWalking,都源于Dapper的影响。

    为了有效地进行分布式追踪,Dapper 提出了“追踪”与“跨度”两个概念。从客户端发起请求抵达系统的边界开始,记录请求流经的每一个服务,直到到向客户端返回响应为止,这整个过程就称为一次“追踪”(Trace)。由于每次 Trace 都可能会调用数量不定、坐标不定的多个服务,为了能够记录具体调用了哪些服务,以及调用的顺序、开始时间、执行时长等信息,每次开始调用服务前都要先埋入一个调用记录,这个记录称为一个“跨度”(Span)。Span 的数据结构应该足够简单,以便于能放在日志或者网络协议的报文头里;也应该足够完备,起码应含有时间戳、起止时间、Trace 的的 ID、当前 Span 的 ID、父 Span 的 ID 等能够满足追踪需要的信息。每一次 Trace 实际上都是由若干个有顺序、有层级关系的 Span 所组成一颗“追踪树”(Trace Tree)


    3

    追踪系统分类

    追踪系统根据数据收集方式的差异,可分为三种主流的实现方式,分别是基于日志的追踪(Log-Based Tracing),基于服务的追踪(Service-Based Tracing)和基于边车代理的追踪(Sidecar-Based Tracing)

    MDC则属于基于日志的追踪,可以看出,MDC只有Trace,缺少了Span,虽然可以通过改造实现,但是如果在微服务中,比如Java生态,Spring Cloud Sleuth则是一个开箱可用的工具。基于日志的追踪显然侵入性比较低,性能损耗少,但依赖日志归集,实时性可能并不够,通常需要搭配EFK来食用。

    基于服务的追踪是目前最为常见的追踪实现方式, Zipkin、SkyWalking、Pinpoint 等主流追踪系统使用此种方案。服务追踪的实现思路是通过某些手段给目标应用注入追踪探针(Probe),针对 Java 应用一般就是通过 Java Agent 注入的。这种方式同样侵入性低,但性能消耗略大,但可追踪的信息量大,也更精确。其实,基于服务的追踪可以认为是早期AMP的延续。

    基于SideCar的追踪是服务网格的专属方案,也是最理想的分布式追踪模型,它对应用完全透明,无论是日志还是服务本身都不会有任何变化,它与程序语言无关,而且有独立的数据通道,当然缺点就是运维复杂,还不够普及.而且基于SideCar的追踪方案其实也需要依赖Zipkin或SkyWalking等具体方案做UI和存储。

    为了规范各种追踪产品,让其实现兼容和适配,2016年的时候,CNCF弄了个OpenTracing规范,主流的追踪系统目前都支持OpenTracing规范。现如今,OpenTracing规范已经升级为OpenTelemetry。

    分布式追踪系统前世今生我们讲完了,至于技术选型,则看各位量体裁衣了。

    长按识别二维码  关注我

    查看更多精彩内容


    文章转载自互联网活化石,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论