简介
分布式链路跟踪中的traceid和spanid代表什么?在分布式链路跟踪中有两个重要的概念:跟踪(trace)和 跨度( span)。从客户端发起请求抵达系统的边界开始,记录请求流经的每一个服务,直到向客户端返回响应为止,这整个过程就叫做一次Trace。由于每次 Trace 都可能会调用数量不定、坐标不定的多个服务,那么为了能够记录具体调用了哪些服务,以及调用的顺序、开始时点、执行时长等信息,每次开始调用服务前,系统都要先埋入一个调用记录,这个记录就叫做一个Span。
- 在整个请求的调用链中,请求会一直携带 traceid(由客户端或网关生成) 往下游服务传递,每个服务内部也会生成自己的 spanid 用于生成自己的内部调用视图,并和traceid一起传递给下游服务。traceid 在请求的整个调用链中始终保持不变,所以在日志中可以通过 traceid 查询到整个请求期间系统记录下来的所有日志。
- 请求到达每个服务后,服务都会为请求生成spanid,而随请求一起从上游传过来的上游服务的 spanid 会被记录成parent-spanid或者叫 pspanid。当前服务生成的 spanid 随着请求一起再传到下游服务时,这个spanid 又会被下游服务当做 pspanid 记录。
在 A->B->C->D 的情况下,在整个调用链中,正常情况下会产生 3 个 Span,分别是 Span1(A->B)、Span2(B->C)、Span3(C->D),这时 Span3 的父 Span 就是 Span2,而 Span2 的父 Span 就是 Span1。
Span 的数据结构应该足够简单,以便于能放在日志或者网络协议的报文头里;也应该足够完备,起码要含有时间戳、起止时间、Trace 的 ID、当前 Span 的 ID、父 Span 的 ID 等能够满足追踪需要的信息。
分布式追踪的特征
- 从目标来看,链路追踪的目的是为排查故障和分析性能提供数据支持,系统在对外提供服务的过程中,持续地接受请求并处理响应,同时持续地生成 Trace,按次序整理好 Trace 中每一个 Span 所记录的调用关系,就能绘制出一幅系统的服务调用拓扑图了。这样,根据拓扑图中 Span 记录的时间信息和响应结果(正常或异常返回),我们就可以定位到缓慢或者出错的服务;然后,将 Trace 与历史记录进行对比统计,就可以从系统整体层面分析服务性能,定位性能优化的目标。
- 从实现来看,为每次服务调用记录 Trace 和 Span,并以此构成追踪树结构,看起来好像也不是很复杂。然而考虑到实际情况,追踪系统在功能性和非功能性上都有不小的挑战。
- 功能上的挑战来源于服务的异构性,各个服务可能会采用不同的程序语言,服务间的交互也可能会采用不同的网络协议,每兼容一种场景,都会增加功能实现方面的工作量。
- 非功能性的挑战,具体就来源于以下这四个方面:
- 低性能损耗:分布式追踪不能对服务本身产生明显的性能负担。
- 对应用透明:追踪系统通常是运维期才事后加入的系统,所以应该尽量以非侵入或者少侵入的方式来实现追踪,对开发人员做到透明化。
- 随应用扩缩:现代的分布式服务集群都有根据流量压力自动扩缩的能力,这就要求当业务系统扩缩时,追踪系统也能自动跟随,不需要运维人员人工参与。
- 持续的监控:即要求追踪系统必须能够 7x24 小时工作,否则就难以定位到系统偶尔抖动的行为。
数据收集
数据收集的三种主流实现方式
- 基于日志的追踪,思路是将 Trace、Span 等信息直接输出到应用日志中,然后随着所有节点的日志归集过程汇聚到一起,再从全局日志信息中反推出完整的调用链拓扑关系。
- 基于服务的追踪,通过某些手段给目标应用注入追踪探针(Probe),比如针对 Java 应用,一般就是通过 Java Agent 注入的。
- 基于边车代理的追踪