Skip to content

面试速答(先看这里)

**一句话结论:**一次请求之间要经过很多的系统,那么如何追踪一次请求从头到尾的流程,就至关重要,这样可以帮我们做很好的链路分析,及问题定位。

60秒标准回答:

随着业务量的增长,为了提升整体系统的可用性、性能及可扩展性,很多大型互联网公司都会采用微服务架构,一次业务请求,一般要经过几个微服务调用才能完成

一次请求之间要经过很多的系统,那么如何追踪一次请求从头到尾的流程,就至关重要,这样可以帮我们做很好的链路分析,及问题定位

在业内,有很多链路追踪的工具,如Google的dapper、twitter的zipkin、京东的hydra、大众点评的cat,以及开源的skywalking。建议大家看看Google的这篇论文,可以说它是分布式链路追踪的启蒙之作

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **生成TraceId:**想要追踪一个完成的链路,就需要有一个标识来标记这次调用链,业内把他叫做traceId,一般会在入口处生成一个全局唯一的traceId,比如说在HTTP的请求入口,在定时任务的调度入口等,生成一个traceId。
  • **传递TraceId:**在有了一个traceId之后,想要通过它把一次调用过程串联起来,那么就需要所有的系统间调用都得把他传递下去,这里面的调用包括了HTTP请求、RPC请求、MQ消息等,甚至还需要涉及到Redis、MySQL等等可能都需要进行传递。

**记忆锚点:**traceId → ThreadLocal → HTTP → TraceId → Google → RPC

关键取舍:

  • 随着业务量的增长,为了提升整体系统的可用性、性能及可扩展性,很多大型互联网公司都会采用微服务架构,一次业务请求,一般要经过几个微服务调用才能完成,。

易错提醒:

  • ThreadLocal咋传递呢,这就要用到TTL了: 前面提到了traceId,其实光有traceId还不够,trace帮我们把一次调用串联起来,但是一次调用在每一个系统上都干了什么,干了多久,成功还是失败,这些对我们来说也很重要,这些信息就被记录在span中。
  • StatusCode:响应状态,标识当次调用是成功或失败。

加分表达:

  • 建议大家看看Google的这篇论文,可以说它是分布式链路追踪的启蒙之作: 不管是哪个实现,在我看来重点就是解决两个问题: 1、生成一个全局的traceId 2、把这个traceId传递下去 生成TraceId 想要追踪一个完成的链路,就需要有一个标识来标记这次调用链,业内把他叫做traceId,一般会在入口处生成一个全局唯一的traceId,比如说在HTTP…
  • 但是,如果有多线程怎么办呢?

追问准备:

  • 围绕「traceId」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「ThreadLocal」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「HTTP」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

随着业务量的增长,为了提升整体系统的可用性、性能及可扩展性,很多大型互联网公司都会采用微服务架构,一次业务请求,一般要经过几个微服务调用才能完成,。

一次请求之间要经过很多的系统,那么如何追踪一次请求从头到尾的流程,就至关重要,这样可以帮我们做很好的链路分析,及问题定位。

在业内,有很多链路追踪的工具,如Google的dapper、twitter的zipkin、京东的hydra、大众点评的cat,以及开源的skywalking。建议大家看看Google的这篇论文,可以说它是分布式链路追踪的启蒙之作:https://bigbully.github.io/Dapper-translation/

不管是哪个实现,在我看来重点就是解决两个问题:

1、生成一个全局的traceId

2、把这个traceId传递下去

生成TraceId ​

想要追踪一个完成的链路,就需要有一个标识来标记这次调用链,业内把他叫做traceId,一般会在入口处生成一个全局唯一的traceId,比如说在HTTP的请求入口,在定时任务的调度入口等,生成一个traceId。

传递TraceId ​

在有了一个traceId之后,想要通过它把一次调用过程串联起来,那么就需要所有的系统间调用都得把他传递下去,这里面的调用包括了HTTP请求、RPC请求、MQ消息等,甚至还需要涉及到Redis、MySQL等等可能都需要进行传递。

所以,想要实现一个链路追踪,需要很多中间件一起配合才行,通常这个traceId会存放在RPC的请求头、HTTP的请求头、MQ消息的消息头中进行传递。

image.png

系统之间通过这种方式,那系统内部也是需要传递的,所以一般都是用ThreadLocal来实现的,在接收到请求后,会把这个traceId存储在ThreadLocal中,然后就能在当前线程中一直传递下去,并且在记录日志的时候取出来打印到日志中,在需要调远程的时候,取出来传递下去。

但是,如果有多线程怎么办呢?ThreadLocal咋传递呢,这就要用到TTL了:

📄 ✅有了InheritableThreadLocal为啥还需要TransmittableThreadLocal?

打开文档:✅有了InheritableThreadLocal为啥还需要TransmittableThreadLocal?

扩展知识 ​

Span ​

前面提到了traceId,其实光有traceId还不够,trace帮我们把一次调用串联起来,但是一次调用在每一个系统上都干了什么,干了多久,成功还是失败,这些对我们来说也很重要,这些信息就被记录在span中。

通常一个完整的 Span 具有如下属性:

  • Operation Name:描述了当前接口的行为语义,如具体的哪个接口,哪个URL地址。
  • SpanId/ParentSpanId:接口调用的层级标识,用于还原 Trace 内部的层次调用关系。
  • Start/FinishTime:接口调用的开始和结束时间,二者相减就是该次调用的耗时。
  • StatusCode:响应状态,标识当次调用是成功或失败。
  • Tags & Events:调用附加信息

image.png

​