双十一你下单后,最想做的事情是什么?打开物流 App 查“快递到哪了”。物流系统为什么能回答?因为包裹从揽收到派送的每一站,都有人扫一下运单号,留下一条可查询的记录。把这些记录按时间串起来,就是一件快递的完整旅程。软件世界也一样:一个用户请求进到系统后,会经历“前端服务 → 网关 → 订单服务 → 库存服务”的层层传递。一旦页面变慢,就像快递中途丢了——你只知道“还没到”,却不知道卡在哪个中转站。分布式追踪就是给每个请求贴一张“运单号”(Trace ID),让它在每个服务停留时留下一条“扫描记录”(Span),最终拼出完整旅程(Trace)。而 OpenTelemetry Collector,就是这套体系的“物流数据总站”:所有网点的扫描数据先汇到这里,统一清洗、采样、转发,再送到 Jaeger 或 Zipkin 这类“物流大屏”上展示。这篇文章不堆术语,就用“寄快递”这一个故事,把 Trace、Span、上下文传播、Collector 架构、与 Jaeger/Zipkin 的集成全部讲透。

岗位关联

对 SRE 和 AI 工程师而言,分布式追踪是排查微服务性能问题的“第一现场”;掌握 OpenTelemetry Collector,就能以统一口径搭建全链路可观测体系,让每一次故障定位从“考古翻日志”变成“看轨迹图”。

目录
  1. 可视化演示
  2. 追踪基础概念
  3. 上下文传播
  4. Collector 架构
  5. 数据收集优化
  6. Jaeger 集成
  7. Zipkin 集成
  8. 统一可观测性
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示微服务调用链路追踪、OTel Collector三段式管道及多数据类型集成的三模态交互动画。

本节配有交互动效(进入视口后自动加载,约 3KB)。

分布式追踪调用链路 Trace Span Trace=ΣSpan 请求穿越多个微服务每个服务产生Span通过上下文传播关联为完整Trace

追踪基础概念

假设你在杭州寄出一件快递到北京。揽收员扫码,系统生成一个运单号;到了杭州分拨中心,再扫一次;到了北京转运中心,又扫一次;最后派送员签收,再扫一次。每一次扫描只记录“这一站在处理什么、花了多久”。在物流术语里,这件快递从寄出到签收的全部扫描记录,就是一条“完整旅程”。

分布式系统也一样。用户点一下“下单”,请求先打到前端服务,前端调用网关,网关调用订单服务,订单服务再查库存。我们把一次完整请求经过的所有服务调用,叫做一条 Trace(追踪);其中每一次独立的服务处理,叫做一个 Span(跨度)。Span 就像那条扫描记录,记着:在哪个服务、什么时候开始、什么时候结束、它由谁发起。

Span 是 Trace 的基本单位。每个 Span 有四个关键信息:服务名(name)、自己的 ID(spanId)、父 Span 的 ID(parentId)、起止时间(startTime/endTime)。根 Span 没有父亲,它下面挂着一串子 Span,子 Span 下面还可能再挂孙 Span——所有 Span 按 parentId 组织起来,就形成一棵“调用树”。这就是为什么 Trace 在数学上可以写成一组 Span 的集合。

你还可以把 Trace 想象成快递的完整轨迹页:运单号是这页的标题,每个扫描点(Span)是页面上的一行记录,每行都写着上一站是谁(parentId)。物流客服之所以能三秒回答“到哪了”,就是因为这页轨迹被完整保存着。分布式系统里,SRE 能快速定位慢请求,同样靠这份“轨迹页”。

Trace={Span1, Span2, …, Spann}\displaystyle \text{Trace} = \{ \text{Span}_1,\ \text{Span}_2,\ \ldots,\ \text{Span}_n \}
这条公式说明:一条 Trace 不是某个神秘实体,而是“所有 Span 的集合”。就像快递轨迹页 = 所有扫描记录的集合。
  1. Trace一条完整请求链路,相当于一件快递从寄出到签收的全过程记录。
    Trace\text{Trace}
  2. Span_i链路中的单个环节,相当于快递在某站的扫描记录,记录这一刻谁在处理、处理了多久。
    Spani\text{Span}_i
  3. 集合花括号整条 Trace 就是所有 Span 按父子关系组成的集合,就像快递旅程是所有站扫描记录的集合。
    {Span1, Span2, …, Spann}\{ \text{Span}_1,\ \text{Span}_2,\ \ldots,\ \text{Span}_n \}

上下文传播

快递能走完全国,靠的是面单上的运单号。包裹每到一个网点,工作人员扫面单确认“这是哪件快递”,然后把面单原封不动传给下一站。如果哪个环节把单号抄错了,这件快递就成了“无头件”,再也查不到轨迹。在分布式追踪里,这个“运单号跟着包裹走”的过程,叫作 上下文传播(Context Propagation)。

服务 A 调用服务 B 时,A 必须把 traceId 和当前的 spanId 装进请求头,B 收到后才能在自己新建的 Span 里填上正确的 traceId 和 parentId,把自己挂到同一棵树上。OpenTelemetry 推荐使用 W3C 标准——在 HTTP 请求里加一个 traceparent 头,这个头就是“电子运单”。

traceparent 长这样:00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01。它用短横线分成四段:版本号(00)、traceId(32 位十六进制,全局唯一)、spanId(16 位十六进制,当前环节)、flags(采样标志,01 表示希望被记录)。下游服务收到后,把自己生成的 spanId 替换进第三段,再传给更下游——运单号就这样一站一站传下去。

这就像快递面单在每站交接时被读一次、更新一次“当前站编号”,但运单号永远不变。只要每个环节都遵守这个格式,无论链路有多深,都能靠 32 位的 traceId 把分散在几十个服务里的 Span 重新串成一条完整轨迹。

traceparent=00−traceId32−spanId16−01\displaystyle \text{traceparent} = \text{00}-\text{traceId}_{32}-\text{spanId}_{16}-\text{01}
traceparent 是“跨服务接力棒”的标准格式。只要每个环节都读写这个头,链路就不会断。
  1. 版本号 00协议版本,目前标准是 00,为未来扩展留出空间。
    00\text{00}
  2. traceId32 个十六进制字符,全局唯一,整条链路共享,就是快递的运单号。
    traceId32\text{traceId}_{32}
  3. spanId16 个十六进制字符,当前环节自己的 ID,下一站会把它当作父 ID。
    spanId16\text{spanId}_{16}
  4. flags(01)采样等控制标志位,01 表示这条链路希望被后端完整记录。
    01\text{01}

Collector 架构

全国网点成千上万,如果每个网点都直接把扫描记录发给物流 App 的数据库,格式五花八门,总部机房也会被请求淹没。于是物流公司建了区域分拨中心:各网点先扫描上报给分拨中心,分拨中心统一整理后再发往总部大屏。这个区域分拨中心,就是 OpenTelemetry Collector。

Collector 是 OpenTelemetry 提供的独立服务,它自己不产生追踪数据,只负责接收、加工、转发。应用侧的 SDK 把 Span 用 OTLP 协议(OpenTelemetry 的默认传输协议,相当于标准化的货车线路)发给 Collector。Collector 经过一条“流水线”处理后,再导出给 Jaeger、Zipkin 或云厂商后端。业务团队从此只需要对接 Collector,不必关心后端是谁。

Collector 的流水线由三部分组成:Receiver(接收器)是管道入口,支持 OTLP、Zipkin 等多种协议;Processor(处理器)是中间加工环节,负责批处理、采样、过滤、脱敏;Exporter(导出器)是管道出口,把数据发给目标后端。数据只能从 Receiver 流向 Exporter,像包裹在分拨中心必须“先进场、再分拣、最后装车”一样,方向从不颠倒。

有了 Collector 这个“区域分拨中心”,即使未来换掉后端大屏(比如从 Zipkin 换成 Jaeger),业务代码一行都不用改,只需在 Collector 配置里换一个 exporter。这就是为什么它成为现代可观测性体系的标准件。

Pipeline=Receiver⟶Processor⟶Exporter\displaystyle \text{Pipeline} = \text{Receiver} \longrightarrow \text{Processor} \longrightarrow \text{Exporter}
Collector 的管道是单向流水线。理解这条公式,就理解了 Collector 的全部职责边界。
  1. Receiver管道入口,相当于分拨中心的收件口,支持 OTLP、Zipkin 等多种协议。
    Receiver\text{Receiver}
  2. Processor中间加工环节,负责批量打包、采样、过滤、脱敏,像分拣线上的质检员。
    Processor\text{Processor}
  3. Exporter管道出口,把处理后的数据发送到 Jaeger 等后端,像装车发运。
    Exporter\text{Exporter}
  4. 管道方向数据只能从 Receiver 流向 Exporter,保证处理顺序固定、不跳站。
    ⟶\longrightarrow

数据收集优化

双十一全网一天几亿件快递,如果总部对每件快递都做“全程视频追踪”,存储成本高到无法想象。物流公司的做法是:网点攒够一车再发走(批处理),同时只对部分快递做深度轨迹分析(采样)。Collector 的 Processor 里,恰好内置了这两板斧。

第一个是 batch 批处理器:把短时间内的多条 Span 打包成一个请求再发送,减少网络往返次数。默认配置下,可能攒够 512 条 Span 或等 5 秒触发一次发送。第二个是采样:用随机数决定保留哪条 Trace,常见的有固定比例采样(如保留 10%)和尾部采样(链路结束后按“是否出错”决定保留)。

采样可以写成一条简单的判断:生成一个 0 到 1 之间的随机数 r,当 r 低于保留率 rate 时保留,否则丢弃。错误链路可以配置 100% 保留,保证故障现场不丢。此外 Collector 还内置队列和背压机制:后端变慢时数据先排队,队列满了就丢弃超量的 Span 而不是无限堆积内存——就像分拨中心的传送带满了,宁可拒收也不让包裹堵满仓库。

批处理省流量、采样省存储、队列防雪崩,这三件事分别回答了运维最关心的三个问题:网络贵不贵、存储贵不贵、宕机会不会。数据收集不是“只要全量就好”,而是一门在成本与完整度之间找平衡的艺术。

decision(r)={keep,r<ratedrop,r≥rate\displaystyle \text{decision}(r) = \begin{cases} \text{keep}, & r < \text{rate} \\ \text{drop}, & r \ge \text{rate} \end{cases}
采样就是在“省钱”和“够用”之间做取舍:随机数小于保留率就留下,否则就放弃这条链路。
  1. r(随机数)系统掷出的骰子,取值在 0 到 1 之间。
    rr
  2. rate(保留率)管理员配置的比例,比如 0.1 表示只保留 10% 的链路。
    rate\text{rate}
  3. keep 分支随机数小于保留率时保留这条 Trace,就像抽中“重点追踪”名额。
    r<rate⇒keepr < \text{rate} \Rightarrow \text{keep}
  4. drop 分支否则丢弃,省下存储与带宽,同时保证留下的样本仍有统计代表性。
    r≥rate⇒dropr \ge \text{rate} \Rightarrow \text{drop}

Jaeger 集成

快递数据汇到总部后,管理员怎么看?现实中你会打开物流 App,输入运单号查轨迹。在追踪体系里,这个“物流 App”常常就是 Jaeger——一个开源的分布式追踪后端,提供存储、查询界面和依赖关系图。

Collector 通过 exporter 把处理好的 Span 发给 Jaeger:通常走 OTLP 协议,指到 Jaeger 的 4317 端口。Jaeger 把数据写入 Elasticsearch 或 Cassandra 存储,再通过 jaeger-query 服务提供网页查询。SRE 只需要在搜索框输入 traceId,就能看到这条请求途经的每一个服务。

Jaeger 的查询界面是排查故障的主战场。进入一条 Trace,会看到瀑布图:横向是时间,每个 Span 按“谁调用谁”的层级纵向排列。哪个 Span 的横条最长,瓶颈就在哪个服务。Jaeger 还能生成服务依赖图,展示服务间的调用关系与流量大小,帮助 SRE 一眼看清系统结构。

从“快递轨迹页”的角度看,Jaeger 就是最直观的那一页:运单号查轨迹,每个扫描点按时间画成横条,哪一站延误一眼可见。Collector 负责把数据送到这里,Jaeger 负责让它好看好用。

Collector→OTLPJaeger→Query UISRE\displaystyle \text{Collector} \xrightarrow{\text{OTLP}} \text{Jaeger} \xrightarrow{\text{Query UI}} \text{SRE}
数据流是一根接力棒:Collector 整理完数据,通过 OTLP 交给 Jaeger,SRE 再在浏览器里用 UI 查询。
  1. Collector数据总站,完成清洗与加工的终点站。
    Collector\text{Collector}
  2. OTLP 传输OpenTelemetry 的默认导出协议,像标准化的货车线路。
    →OTLP\xrightarrow{\text{OTLP}}
  3. Jaeger负责存储、索引并提供查询接口的追踪后端平台。
    Jaeger\text{Jaeger}
  4. Query UI 与 SRESRE 在浏览器里输入追踪号,就能看到完整轨迹与瀑布图。
    →Query UISRE\xrightarrow{\text{Query UI}} \text{SRE}

Zipkin 集成

不是所有物流公司都用同一套大屏。有些公司自建系统,用的是另一套成熟方案——Zipkin。Zipkin 是 Twitter 开源的分布式追踪系统,问世比 OpenTelemetry 早得多,很多老项目早已用它埋点。新体系想接老数据,就得靠“翻译”。

OpenTelemetry Collector 内置了 zipkin receiver 和 zipkin exporter:既能接收老系统用 Zipkin 格式上报的数据,也能把 OTLP 数据转成 Zipkin v2 的 JSON 格式再转发给 Zipkin。这意味着哪怕业务代码是五年前用 Zipkin 埋的点,也可以通过 Collector 平滑接入新体系,不必大改代码。

选型对比上:Jaeger 与 OpenTelemetry 同为 CNCF 项目,对 OTLP 原生支持最好,适合新项目;Zipkin 生态更老,在存量系统中占有率更高,但 JSON/HTTP 上报开销稍大。更常见的做法不是二选一,而是让 Collector 同时开多个 exporter——数据各发一份,新系统看 Jaeger,老监控看 Zipkin,过渡期平稳度过。

Collector 像一位熟练的“翻译官”:面单从中文学成英文,再从英文翻回中文,包裹本身(Trace ID)始终没变。有了它,新旧系统共存不是难题,而是配置里的两行代码。

Collector→zipkin v2 (JSON)Zipkin\displaystyle \text{Collector} \xrightarrow{\text{zipkin v2 (JSON)}} \text{Zipkin}
Zipkin 集成本质是“格式翻译”:Collector 把 OTLP 写成的面单翻译成 Zipkin 认识的语言。
  1. zipkin v2Zipkin 当前的 JSON 数据模型版本,是它认识的面单标准格式。
    zipkin v2 (JSON)\text{zipkin v2 (JSON)}
  2. 协议转换Collector 把 OTLP Span 转成 Zipkin 格式再转发,像把中文面单翻译成英文。
    →zipkin v2 (JSON)\xrightarrow{\text{zipkin v2 (JSON)}}
  3. Zipkin 落库与查询Zipkin 负责存储和检索,UI 同样展示瀑布图,方法与 Jaeger 类似。
    Zipkin\text{Zipkin}

统一可观测性

一个成熟的物流公司,光有快递轨迹还不够。调度室要看今天的总运量趋势(什么时候爆仓),仓库要看每件快递的操作备注(为什么滞留),客服要能查每件快递的完整轨迹(到底到哪了)。这三样各司其职,合起来才叫“全程可控”。在 IT 系统里,它们就是可观测性的三大支柱:Metrics、Logs、Traces。

Metrics 是聚合指标:QPS、延迟百分位、错误率,回答“发生了什么趋势”;Logs 是事件明细:一条条带时间戳的错误信息和上下文备注,回答“具体发生了什么”;Traces 是请求路径:一次请求经过哪些服务、每步多久,回答“为什么变慢”。三者通过 traceId 关联起来,构成完整拼图。

OpenTelemetry 的贡献,是把三类信号的采集、传输和导出协议统一了。Collector 可以同时接收 Metrics 与 Traces,日志里也可以携带 traceId 字段。SRE 的排障黄金流程是:先用 Metrics 发现“订单服务 P99 飙升”,再用 Traces 定位“是库存服务这个 Span 最慢”,最后用 Logs 查看“具体是慢 SQL 还是连接池耗尽”。三步缺一不可。

回到物流:Metrics 是日报表,Logs 是交接备注,Traces 是运单轨迹。只装一类的公司都做不到“全程可控”。对 SRE 而言,掌握三支柱协同,才能从一个告警出发,五分钟内回答“哪里坏了、为什么坏、影响多大”——这正是可观测性的终极意义。

Observability=Metrics+Logs+Traces\displaystyle \text{Observability} = \text{Metrics} + \text{Logs} + \text{Traces}
可观测性不是单点工具,而是三类信号相加。就像物流管理 = 日报表 + 交接备注 + 运单轨迹。
  1. Metrics聚合指标,回答“发生了什么趋势”,如每分钟请求量、P99 延迟。
    Metrics\text{Metrics}
  2. Logs事件明细,回答“具体报了什么错”,每条日志通常带时间戳。
    Logs\text{Logs}
  3. Traces调用路径,回答“这个请求经过了哪些服务、每个环节多久”。
    Traces\text{Traces}
  4. 协同相加三类信号按 traceId 关联起来一起使用,才构成完整可观测性。
    Metrics+Logs+Traces\text{Metrics} + \text{Logs} + \text{Traces}

AI 应用场景

微服务故障快速定位

阶段:监控告警与链路分析

解决的问题:一次“下单变慢”的投诉,传统排查要逐台服务器比对日志时间,耗时数小时;引入分布式追踪后,按 traceId 找到整条链路,瀑布图显示“库存服务”单个 Span 耗时 2.8 秒,占整条链路的 90%,问题立刻锁定。

具体案例:某电商 SRE 收到 P1 告警,用户反馈下单接口 P99 从 300ms 飙到 3s。SRE 打开 Jaeger,输入出错请求的 traceId,瀑布图显示调用链“前端 → 网关 → 订单 → 库存 → 数据库”中,数据库 Span 最长。下钻日志发现慢 SQL,优化索引后 P99 回到 320ms。

大规模集群的采集成本治理

阶段:数据降噪与容量规划

解决的问题:上千个 Pod 每个请求都全量上报,一天产生几十 TB 数据,存储成本爆炸。Collector 用 batch 加采样(例如只保留 5% 的慢链路)把数据量降一个数量级,同时保证有问题的链路一条不丢。

具体案例:某金融团队在 Collector 中配置尾部采样:错误链路 100% 保留、正常链路 5% 保留,再配合 batch 处理器攒批发送。改造后监控数据量下降 90%,P99 延迟监控反而更完整,因为所有故障链路都被兜住了。

新老系统统一接入

阶段:数据接入与协议转换

解决的问题:公司部分老系统用 Zipkin 上报,新系统用 OTLP,两边数据对不上。Collector 作统一入口:新系统走 otlp receiver,老 Zipkin 数据走 zipkin receiver,统一处理后导出到同一套 Jaeger,实现全网一条链路。

具体案例:某公司上云后把 Collector 作为独立服务部署,一份 YAML 配置同时接收 OTLP、Zipkin 和 Prometheus 指标,再分发到 Jaeger 与 Prometheus。业务团队无需改代码,就完成了可观测性平台的渐进式统一。

Python 演示

python75 行
"""Mini simulation of distributed tracing using a logistics metaphor."""import timeimport uuid  class Span:    """A span: one scan record at one station."""     def __init__(self, name, span_id, parent_id, trace_id):        self.name = name        self.span_id = span_id        self.parent_id = parent_id        self.trace_id = trace_id        self.start = time.time()        self.end = None     def finish(self):        self.end = time.time()     def duration_ms(self):        return (self.end - self.start) * 1000 if self.end else 0  class SimpleTracer:    """Mini tracer: stores all spans and propagates the trace id."""     def __init__(self):        self.spans = []     def start_span(self, name, parent=None):        trace_id = parent.trace_id if parent else uuid.uuid4().hex[:16]        parent_id = parent.span_id if parent else None        span = Span(name, uuid.uuid4().hex[:16], parent_id, trace_id)        self.spans.append(span)        return span  def wait(seconds):    time.sleep(seconds)  def build_trace(tracer):    """Build a call chain: front -> gateway -> order -> stock."""    root = tracer.start_span("前端请求")  # 根 Span: 生成运单号    wait(0.01)    gateway = tracer.start_span("网关服务", root)  # 把父 Span 传入以继承 traceId    wait(0.02)    order = tracer.start_span("订单服务", gateway)    wait(0.03)    stock = tracer.start_span("库存服务", order)    wait(0.015)    stock.finish()    order.finish()    gateway.finish()    root.finish()    return root  def print_trace(root, spans):    """Print all spans sorted by start time."""    print("Trace ID:", root.trace_id)    print(f"{'服务':<10}{'span_id':<18}{'parent_id':<18}{'耗时(ms)':<10}")    for s in sorted(spans, key=lambda x: x.start):        parent = s.parent_id if s.parent_id else "-"        print(f"{s.name:<10}{s.span_id:<18}{parent:<18}{s.duration_ms():<10.2f}")  def main():    tracer = SimpleTracer()    root = build_trace(tracer)    print_trace(root, tracer.spans)  if __name__ == "__main__":    main()
目的:用约 60 行代码模拟一次分布式请求的追踪过程——前端请求作为根 Span 生成 Trace ID,下游每个服务通过“传入父 Span”继承同一个 Trace ID,最终输出一张按 parent_id 串联的 Span 表格,直观展示“运单号贯穿全程”。
  1. class Span:一条扫描记录,保存服务名、spanId、parentId、traceId 和起止时间。
  2. self.parent_id = parent_id记录“上一站是谁”,这是还原调用树的关键字段。
  3. trace_id = parent.trace_id if parent else uuid.uuid4().hex[:16]有父节点就继承同一个运单号,没有就新建一个,代表全新链路。
  4. gateway = tracer.start_span("网关服务", root)创建子 Span 时把父 Span 传进去,这就是“上下文传播”在代码中的体现。
  5. stock.finish()环节结束,记录结束时间,之后才能计算这个 Span 的耗时。
  6. print_trace(root, tracer.spans)遍历所有 Span,按 parent_id 串联并打印,还原完整 Trace。

常见误区

误区很多初学者看到 Span 也有时间戳、也有属性标签,就以为分布式追踪只是把日志改个格式。反正都是记录事件,把日志格式换一下不就能做追踪了吗?

正解Trace 的核心不是记录文本,而是通过 traceId、spanId、parentId 构建一棵跨服务的调用树。日志是“每件事的文字备注”,Trace 是“整条路径的地图”:前者解决“发生了什么”,后者解决“经过了谁、花了多久”。两者采集方式、存储模型、查询方式都不同,无法互相替代。物流公司光有各站点的文字备注,没有运单号把它们串起来,就无法回答“快递走到哪了”。

为何常见:Span 确实长得像日志行——有时间、有属性、有名字,初次接触很容易按既有经验归类。

误区以为装好 OpenTelemetry Collector 后,不用改业务代码,所有请求就会自动生成 Trace 并出现在 Jaeger 里。

正解Collector 只是“物流数据总站”,它不会自己寄快递。业务服务必须先用 SDK 或 Agent 做插桩(instrumentation):在入口生成根 Span、在调用下游时注入 traceparent、在结束时上报 Span。真正产生追踪数据的是埋点,也就是“贴运单号”的动作。没有埋点,Collector 收不到任何数据。

为何常见:Collector 名字里有“收集”二字,容易让人误以为它能自动拦截流量;实际上它只处理上游主动送来的数据。

误区觉得全量追踪最踏实,一个请求都不丢,这样排障时万无一失。存储贵一点没关系,数据全最重要。

正解100% 采样在高流量系统里会带来巨大的存储与网络成本,而且绝大多数请求是重复的“正常路径”,价值很低。更优做法是按业务重要性分级采样:错误链路 100% 保留,正常链路保留 5%~10%。这样关键故障不丢,成本可控,统计样本也有代表性。

为何常见:“全量”在直觉上等于“最安全”,人们看不到隐性的存储账单,只有月末接到费用通知时才会醒悟。

误区以为 OpenTelemetry SDK 装上后,只要像以前一样直接发 HTTP 请求,Trace ID 就会自动传给下一个服务。

正解上下文传播需要传输层配合。用 HTTP 时,要用支持 W3C traceparent 的库(如 OkHttp、Requests 的 instrumentation),或在请求头里手动注入与提取;涉及消息队列、跨线程、异步回调时,还要额外设置 Context 传递。就像快递面单不会凭空出现,必须有人把运单号写到包裹上,再在交接时交给下一站。

为何常见:SDK 的“自动埋点”宣传让新手高估了自动化的边界,以为框架把所有细节都处理好了。

面试题

Q1. 分布式追踪中,一次完整请求的调用链在术语上被称为什么?

单选题 · easy

  • A.一个 Span
  • B.一条 Trace← 正确答案
  • C.一条日志
  • D.一个指标

答案:B。Trace 是完整旅程,Span 是旅程中的一个环节。选项 A 是局部,C 和 D 属于另两支柱,都不对。就像整条快递轨迹叫“旅程”,单次扫描只是一条“记录”。

Q2. 还原调用先后与父子关系,主要依赖哪些字段?

单选题 · easy

  • A.服务名与端口
  • B.标签与注释
  • C.traceId 与 parentId← 正确答案
  • D.时间戳与版本

答案:C。traceId 保证所有 Span 属于同一趟旅程,parentId 指明“上一站是谁”,二者共同拼出调用树。服务名、标签、时间戳只是属性,不是串联关系的关键。就像快递靠运单号和“上一站编号”还原路径,而不是靠重量和备注。

Q3. W3C traceparent 头的第四段(如 01)起什么作用?

单选题 · mid

  • A.标记采样控制标志← 正确答案
  • B.保存完整日志内容
  • C.存储服务节点地址
  • D.指定后端存储类型

答案:A。traceparent 四段依次是版本号、traceId、spanId、flags,第四段是采样等控制标志,01 表示希望被记录。它只是一面小旗子,不保存日志、地址或存储信息。

Q4. OpenTelemetry Collector 流水线的正确顺序是?

单选题 · mid

  • A.Receiver 到 Exporter 到 Processor
  • B.Receiver 到 Processor 到 Exporter← 正确答案
  • C.Exporter 到 Receiver 到 Processor
  • D.Processor 到 Receiver 到 Exporter

答案:B。数据像包裹进场一样单向流动:先由 Receiver 收件,再由 Processor 分拣加工,最后 Exporter 装车发出。顺序颠倒会导致数据未加工就发出,或根本没有入口接收。

Q5. 下列哪个处理器负责攒批发送以节省网络开销?

单选题 · mid

  • A.batch 批处理器← 正确答案
  • B.内存限制器
  • C.属性过滤器
  • D.重命名器

答案:A。batch 把多条 Span 打包成一批再发送,减少网络往返次数。内存限制器防 OOM,属性过滤器改标签,重命名器改字段名,它们都不负责攒批。就像网点攒够一车再发,而不是一件一件单独送货。

Q6. 关于追踪采样,正确的做法是?

多选题 · mid

  • A.错误链路建议 100% 保留← 正确答案
  • B.正常链路按比例采样← 正确答案
  • C.采样率越高存储成本越低
  • D.采样与存储成本不相关

答案:AB。错误链路是故障现场,通常全量保留;正常链路按 5%~10% 采样即可控制成本。采样率越高存储成本越高,所以 C、D 都不对。这就像公司只对重点包裹做全程监控,普通包裹抽查即可。

Q7. Jaeger 在追踪体系中的职责是?

单选题 · easy

  • A.业务数据库存储
  • B.追踪存储与查询 UI← 正确答案
  • C.流量负载均衡器
  • D.消息队列中间件

答案:B。Jaeger 是追踪后端,负责存储 Span 并提供网页查询。它不存业务数据、不分流流量、不传递消息。类比物流 App:它不运包裹,只负责让你查到包裹到哪了。

Q8. Jaeger 与 Zipkin 对比,正确的是?

多选题 · hard

  • A.Jaeger 对 OTLP 支持更原生← 正确答案
  • B.Zipkin 在存量系统更常见← 正确答案
  • C.两者都不能做协议转换
  • D.Collector 可同时导出给两者← 正确答案

答案:ABD。Jaeger 与 OpenTelemetry 同为 CNCF 项目,OTLP 原生支持更好;Zipkin 问世更早,老系统里更常见;协议转换由 Collector 完成,所以 C 错误。Collector 可以同时开多个 exporter,让新旧两套大屏并行显示。

Q9. 关于头部采样与尾部采样,正确的是?

多选题 · hard

  • A.头部采样在请求入口就决定← 正确答案
  • B.尾部采样在链路结束后决定← 正确答案
  • C.头部采样能兜底所有错误链路
  • D.尾部采样实现开销通常更小

答案:AB。头部采样在入口随机决定,无法预知这条链路后面会不会出错,所以不能兜底错误链路;尾部采样等链路结束后再依据错误情况决定,能更好保留故障,但需要缓冲整条链路,开销更大。因此 C、D 错误。就像是在寄出时就决定要不要跟拍,还是等包裹到了再决定补看全程监控。

Q10. SRE 用三大支柱协同排障的正确顺序是?

多选题 · hard

  • A.先用 Metrics 找到异常服务← 正确答案
  • B.再用 Traces 定位慢调用链← 正确答案
  • C.最后用 Logs 查看具体报错← 正确答案
  • D.只用 Metrics 就能定位根因

答案:ABC。标准排障流程是“指标圈范围 → 链路找路径 → 日志看细节”:Metrics 告诉你哪个服务异常,Traces 告诉你哪个 Span 慢,Logs 告诉你具体报错原因。Metrics 只能指出趋势,无法给出根因,所以 D 错误。就像物流调度先看哪个区域爆仓,再查那件快递卡在哪个站点,最后看操作备注。