链路聚合手工模式的缺点(从MDC说分布式链路追踪的前世今生)

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

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

这个时候,可能就需要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里最早使用的全链路方案,最简单,也最有效。

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)

链路聚合手工模式的缺点(从MDC说分布式链路追踪的前世今生)(1)

Dapper原理

追踪系统根据数据收集方式的差异,可分为三种主流的实现方式,分别是基于日志的追踪(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。

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

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页