Skip to content

链路跟踪

在分布式系统中,我们基于业务划分服务,并对外暴露服务访问接口。
试想这样一个场景,如果我们发现某一个业务接口在访问过程中发生了错误,一般的处理过程就是快速定位到问题所发生的服务并进行解决。
但在现实的中大型系统中,一个业务接口背后可能会调用一批其他业务体系中的业务接口或基础设施类的底层接口,这时候我们如何能够做到快速定位问题呢?

传统的做法是通过查阅服务器的日志来定位问题,但在中大型系统中,这种做法可操作性并不强,主要原因在于我们很难找到包含错误日志的那台服务器。
一方面,开发人员可能都不知道整个服务调用链路中具体有几个服务,也就无法找到是哪个服务发生了错误。
就算找到了目标服务,在分布式集群的环境下,我们也不建议直接通过访问某台服务器来定位问题。服务监控的需求就应运而生。

从上述描述中,我们不难看出服务监控是分布式系统的基础需求之一。
可以说,链路跟踪是构建分布式系统过程中必不可少的一个技术组件

关于分布式服务跟踪和监控的运行原理,实际上并不是特别复杂,我们首先需要引入两个基本概念,即 Trace 和 Span。

其中,Trace 代表一次请求和响应过程中的整个调用链路,而 Span 则代表这个链路中的一段跨度
显然,Trace 和 Span 是一对多的关系。通过将一个个具体的 Span 聚合起来,我们就可以构建出一条完整的链路并进行有效的跟踪。
围绕这两个概念,我们可以进一步引出链路跟踪过程中的 4 种关键事件并开展性能分析。

分布式服务跟踪基本原理

我们先来分析一下分布式服务跟踪的基本原理,这里需要引入两个 Id,即 TraceId 和 SpanId,它们分别代表一个全局唯一的 Trace 和 Span。

TraceId 即跟踪 Id。在分布式架构中,每个请求会生成一个全局的唯一性 Id,通过这个 Id 可以串联起整个调用链。
也就是说,当请求在分布式系统内部流转时,系统需要始终保持传递该唯一性 Id,直到请求返回。这个唯一性 Id 就是 TraceId。

除了 TraceId 外,我们还需要 SpanId,SpanId 一般被称为跨度 Id
所谓跨度,就是调用链路中的其中一段时间,具有明确的开始和结束这两个节点,这样通过计算开始时间和结束时间之间的时间差,我们就能明确调用过程在这个 Span 上所产生的时间延迟。

通过前面的介绍,我们不难理解 TraceId 和 SpanId 之间是一对多的关系,即在一个调用链路中只会存在一个 TraceId,但会存在多个 SpanId。
这样多个 SpanId 之间就会有父子关系,即链路中的前一个 SpanId 是后一个 SpanId 的父 SpanId,如下图所示:

理解了 TraceId 和 SpanId 的概念之后,我们就需要对整个分布式调用链路进行进一步拆分,从而细化控制的粒度。
业界一般通过四种不同的注解(Annotation)记录每个服务的客户端请求和服务器响应过程
这里的注解实际上代表的就是链路中所发生的关键事件。

  • cs 注解。cs 代表 Client Send,即客户端发送请求,启动整个调用链路。
  • sr 注解。sr 代表 Server Receive,即服务端接收请求。显然,(sr - cs)值代表请求从客户端到服务器端所需要的网络传输时间。
  • ss 注解。ss 代表 Server Send,即服务端把请求处理结果返回给客户端。(ss - sr)值代表服务器端处理该请求所需要的时间。
  • cr 注解。cr 代表 Client Receive,即客户端接收到服务端响应,结束调用链路。(cr - sr)值代表响应结果从服务器端传输到客户端所需要的时间。

下面结合一张示意图来进一步解释这四种注解之间的关联关系:

有了这四个注解之后,我们就可以使用它们来量化整个服务调用链路,从而找出潜在的问题。这里同样给出一个示意图:

在上图中,我们看到这次请求的 TraceId 是 trace1,而 SpanId 根据不同的服务会发生变化。
这里的四种注解构成了客户端和服务器对一次请求处理的闭环。
对于服务 A 而言,cs 是 11:10:44, cr 是 11:10:55,也就说该次服务请求经由服务 A 的整个调用链路时间是 11s(11:10:44 - 11:10:55),显然这个响应时间非常长。

显然,通过这些注解我们就可以发现服务调用链路中存在的问题,目前主流的服务监控实现工具都对这些注解做了支持和封装。