想象你是跨城快递公司的客服主管。客户来电:“我的包裹三天了还没到!” 你如果只盯着出发站和终点站的自动分拣记录(就像只盯服务器 CPU 和内存),根本不知道包裹卡在了哪个分拣场。你需要一张“全程物流单”:几点从深圳发出、上了哪辆货车、在哪个分拣中心停留多久、谁经手的。这套完整旅程记录,就是分布式系统的“链路追踪”。 在微服务世界,一次点击会像接力棒一样穿过网关、订单、支付、库存十几个服务。任何一个环节变慢,用户感知到的都是“卡了”。怎么快速揪出元凶?答案就是给每个请求贴一张“快递单号”(TraceID),在每道分拣口(微服务)盖上一个“时间戳包裹卡”(Span),再让所有快递公司遵守同一套“国标面单”(OpenTelemetry),最后用“物流查询网站”(Jaeger)把整条路线画出来。本文就用这套快递物流的比喻,带你从零搭建完整的可观测性体系。

岗位关联

对 SRE 和平台架构师而言,掌握分布式追踪意味着从“逐台服务器翻日志”升级为“一眼看清全链路”,这是大型微服务故障定位、容量评估与性能优化的核心能力。

目录
  1. 可视化演示
  2. 可观测性三大支柱
  3. OpenTelemetry统一规范
  4. 追踪数据模型
  5. 采样策略
  6. Jaeger存储方案
  7. Zipkin选型对比
  8. 全链路落地实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示OpenTelemetry统一采集到Jaeger/Zipkin后端的追踪架构,可视化Trace/Span调用链路和采样策略对比。

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

分布式追踪调用链路 请求从网关→服务A/B→数据库→服务C/D→返回 每个服务调用对应一个Span,组成完整Trace Span_Dur=t_end−t_start Span持续时间衡量服务调用耗时

可观测性三大支柱

快递客服中心想高效处理“包裹去哪了”的投诉,手里需要三样东西:客服通话录音(日志),用来查单个包裹当时发生了什么;运输大屏(指标),用来判断全网业务量是否异常;每单的完整轨迹页(追踪),用来精确还原这个包裹走过的每一步。缺了任何一样,客服都只能靠猜。

微服务系统也一样。日志(Logging)回答“某台机器上发生了什么”,指标(Metrics)回答“整体健康状态如何”,而链路追踪(Tracing)回答“一个请求到底经过了哪些服务、每步花了多久”。前两种能力发展多年,追踪则是云原生时代补齐的最后一块拼图。

为什么必须补这块?因为服务一多,调用关系呈平方级爆炸。n 个服务理论上就有 C(n,2) 种可能调用关系;一次请求可能同时命中多种关系,出问题时仅靠两端日志对齐时间线,几乎不可能定位。

追踪就像给每个“包裹”贴了物流单,能按单号还原完整旅程,它也因此被公认为三大支柱之一:日志回答“发生了什么”,指标回答“有多严重”,追踪回答“到底坏在哪一环”。

Ncall=C(n,2)=n×(n−1)2\displaystyle N_{\text{call}} = C(n,2) = \frac{n \times (n-1)}{2}
通俗解释:10 个微服务就有 45 种潜在调用关系,人肉推演早就不现实,必须靠系统自动记录。
  1. n 个微服务参与调用的服务总数,例如订单、支付、库存等 10 个服务,则 n=10
    n=10n = 10
  2. 组合数 C(n,2)从 n 个服务中任选 2 个的配对数,表示最多可能存在的调用关系数量
    (n2)=n×(n−1)2\binom{n}{2} = \frac{n \times (n-1)}{2}
  3. 代入计算乘法和除法依次执行,得到潜在调用关系总数
    10×92=45\frac{10 \times 9}{2} = 45

OpenTelemetry统一规范

早期物流业各家面单格式五花八门,A 快递的单号在 B 快递的扫描枪上读不出来。国家推出统一面单标准后,任何分拣中心都能识别信息,包裹跨公司转运才顺畅。观测数据的世界也经历过同样的混乱。

早期追踪 SDK 各自为政:用 Jaeger 埋的点,换 Zipkin 就得重写;Prometheus 的指标格式,InfluxDB 又不认。OpenTelemetry 就是观测界的“国标面单”——它由 OpenTracing 与 OpenCensus 合并而来,目标是统一日志、指标、追踪三类信号的采集标准。

技术细节上,OTel 提供统一 API 与 SDK,应用代码只依赖 OTel API 打点;通过 OTLP 协议导出,同一套 SDK 既能把数据发给 Jaeger,也能发给 Zipkin。SDK 还内置采样、批处理、重试等能力,并有 Java Agent、Python 库等多语言自动注入方案。衡量规范是否落地,看覆盖率:

有了“国标面单”,快递公司想换查询网站或新增合作方,都不必重贴面单。统一规范的最大红利正在于此:你完全可以先用 Jaeger,未来平滑切换或共存,而应用代码一行都不用改。

Rcoverage=NadoptedNtotal×100%\displaystyle R_{\text{coverage}} = \frac{N_{\text{adopted}}}{N_{\text{total}}} \times 100\%
通俗解释:所有需要观测的组件中,已经接入 OpenTelemetry 的比例,就像“已经贴了国标面单的包裹 ÷ 所有包裹”。
  1. N_adopted 已接入数量已经用 OpenTelemetry 完成埋点的服务与中间件数量
    NadoptedN_{\text{adopted}}
  2. N_total 总数量需要观测的全部服务、网关、消息队列等组件总数
    NtotalN_{\text{total}}
  3. 相除并转百分数两者相除再乘以 100%,得到落地覆盖率,越高说明标准执行越彻底
    NadoptedNtotal×100%\frac{N_{\text{adopted}}}{N_{\text{total}}} \times 100\%

追踪数据模型

一张物流详情页由三部分组成:快递单号(TraceID)统领整趟旅程;列表里每一行“XX 分拣中心已发出”就是一条 Span;而包裹每到达一个新分拣中心,工作人员都要先扫码确认“上一站是谁、单号多少”,这就是 SpanContext 在完成上下文传播。

技术定义上,Trace 是一次完整请求的树状结构;每个 Span 代表一个服务调用单元,包含服务名、开始时间、耗时、状态与父子关系。SpanContext 是 Span 的“身份证”,装着 TraceID、SpanID 和采样标记。

关键机制是跨服务传播:服务 A 调用服务 B 时,A 把 SpanContext 序列化进 HTTP 头(如 W3C 的 traceparent: 00-<traceid>-<spanid>-01),B 收到后从 Header 中恢复上下文,把自己挂成 A 的子节点。任何一环没传递,链路就从那里断裂。用户感知的总耗时与各 Span 的关系如下:

就像包裹每过一个分拣口都要重新扫码,SpanContext 就是接力棒本身。理解 Trace–Span–SpanContext 三位一体,你就拿到了读懂任何追踪系统 UI 与数据的通用语言。

Ttotal=Troot≤∑i=1nTi\displaystyle T_{\text{total}} = T_{\text{root}} \le \sum_{i=1}^{n} T_{i}
通俗解释:用户感受到的总耗时等于最外层“根 Span”的时长,它通常小于所有 Span 的简单相加,因为子任务存在并行。
  1. 总耗时等于根 Span 时长请求从发出到收到响应的真实时间,就是最外层、跨度最大的根 Span
    Ttotal=TrootT_{\text{total}} = T_{\text{root}}
  2. 所有 Span 求和把每个服务调用的耗时全部相加,并行执行的子 Span 会被重复计入
    ∑i=1nTi\sum_{i=1}^{n} T_{i}
  3. 比较关系 ≤因为并行与嵌套重叠,总和通常大于根 Span;只有完全串行时二者才相等
    Troot≤∑i=1nTiT_{\text{root}} \le \sum_{i=1}^{n} T_{i}

采样策略

快递公司想统计大促时效,不需要入库每一件包裹,抽样 10% 的面单就能算出全国平均时效;但客服投诉件必须 100% 单独留档。这就是“抓大放小、保异常”的采样哲学。

全量追踪的数据量会随调用深度成倍放大:一次请求可能产生几十个 Span,日存储轻松上 TB。采样策略就是在“数据完整度”与“存储成本”之间做工程取舍。

头部采样:请求刚进入时用随机数决定是否采样,开销小,但可能漏掉偶发慢请求。尾部采样:先把所有 Span 放入缓冲,请求结束后按结果(是否报错、是否超时)决定整条链路的去留,不会漏掉异常,但缓冲区成本高。生产常用的混合方案是:正常流量 p=0.05~0.1,异常请求 100% 保留。存储成本可以这样估算:

优秀的快递运营绝不会把仓库堆满所有包裹,而是科学抽样加重点件全保。理解采样策略,是你在成本与可观测性之间做工程取舍的分水岭。

Cstore=N×S×p\displaystyle C_{\text{store}} = N \times S \times p
通俗解释:每天处理的包裹总量 × 每个包裹档案体积 × 抽检留档比例,就是档案库每天新增的空间。
  1. N 每日请求总数一天从网关进入的请求数量,相当于一天处理的包裹数
    NN
  2. S 单条 Trace 体积一条完整调用链序列化后占用的存储大小
    SS
  3. p 采样率决定保留多少比例请求的系数,取值在 0 到 1 之间
    0<p≤10 < p \le 1
  4. 三项相乘乘积就是追踪数据日均新增量;采样率降为 1/10,存储成本同步降为 1/10
    N×S×pN \times S \times p

Jaeger存储方案

物流查询网站本身不送快递,它只负责把全国分拣口上报的扫描记录合并展示。Jaeger 正是这样的“查询平台”:应用把 Span 上报给 Collector,Collector 清洗后写入后端存储,Query 服务再检索出来画成瀑布图。

架构组件包括:Agent(或 OTel Collector)负责就近接收与转发;Collector 负责校验、采样与批处理;Storage 是可插拔的后端;UI 与 Query 提供检索和可视化。Jaeger 不绑定存储,适配 Elasticsearch、Cassandra、ClickHouse、Badger 等。

选存储前先算容量,日增数据量可以用下面公式估算。高峰每秒 5000 请求、采样 10%、单条 1KB,一天约 43GB,再预留索引膨胀和查询开销,通常按 2~3 倍规划。

就像物流档案库要按历史单量扩容,追踪存储要按公式倒推。容量算错了,链路数据要么被写满丢弃,要么查询慢如龟速,可观测性体系会从“利器”变成“负担”。

Dday=R×p×s×86400\displaystyle D_{\text{day}} = R \times p \times s \times 86400
通俗解释:每秒收多少包裹 × 留档比例 × 每份档案体积 × 一天总秒数,就能算出档案仓库每天要预留多大空间。
  1. R 每秒请求数峰值或平均每秒到达的请求量(QPS),决定流量规模
    RR
  2. p 采样率与 s 单条体积保留比例与单条 Trace 的存储大小
    p×sp \times s
  3. 86400 一天秒数把每秒的量换算到全天,来自 24 小时 × 3600 秒
    24×3600=8640024 \times 3600 = 86400
  4. 连乘得到日增量四者相乘就是每天新增的存储量,据此选择存储后端与保留期
    Dday=R×p×s×86400D_{\text{day}} = R \times p \times s \times 86400

Zipkin选型对比

打开手机,物流 App 有“菜鸟”也有“快递 100”,功能大同小异。Jaeger 和 Zipkin 就是追踪分析领域的两个“查询网站”,都能消费“国标面单”(OpenTelemetry)导出的数据。

背景上,Zipkin 由 Twitter 开源,是分布式追踪的开山之作;Jaeger 由 Uber 开源,后进入 CNCF。两者数据模型同源,都受 Google Dapper 论文启发,且都兼容 OpenTelemetry,所以不像想象中那样水火不容。

差异主要在细节:UI 上 Jaeger 的 Trace 对比、深度检索更丰富;存储上 Jaeger 支持更多后端(Elasticsearch、Cassandra、ClickHouse),Zipkin 主要依赖 Elasticsearch 与 Cassandra;部署上 Zipkin 单容器更轻,Jaeger 组件更全。理性的选型可以用加权打分:

换查询网站不换面单,正是统一规范带给你的自由。对多数新项目,Jaeger 生态更完整;对存量 Zipkin 体系,也不必立即迁移,先把 OpenTelemetry 规范统一到位,比纠结工具更关键。

Score=∑k=13wk×gk\displaystyle \text{Score} = \sum_{k=1}^{3} w_{k} \times g_{k}
通俗解释:把“功能完整度、存储兼容性、运维成本”三项分别打分,再按团队重视程度加权求和,总分高者胜出。
  1. 三个评分维度 g_k功能完整性、存储兼容性、运维成本,每项按团队需求打 1 到 5 分
    g1, g2, g3g_{1},\ g_{2},\ g_{3}
  2. 权重 w_k团队对每个维度的重视程度,三者权重相加等于 1
    w1+w2+w3=1w_{1} + w_{2} + w_{3} = 1
  3. 加权求和各项得分乘权重后累加,得到该工具的综合得分
    ∑k=13wk×gk\sum_{k=1}^{3} w_{k} \times g_{k}

全链路落地实践

有了面单标准、查询网站和抽样规则,最后一步是让每一家分包快递都真正执行。落地的本质是把规范变成日常习惯:分阶段推进,用覆盖率衡量效果。

落地四步走:第一步,在每个服务引入 OpenTelemetry SDK 并配置导出地址;第二步,为 HTTP、RPC、消息队列框架配置自动注入,让 SpanContext 自然传播;第三步,对关键业务方法手动埋点并补充业务标签;第四步,部署 Jaeger,配置采样率、存储保留期,接入告警平台。

效果如何,用链路完整率说话。该指标低于 95% 时,通常是有服务没配自动注入,或 Header 被网关、框架剥掉了。排查路径是:从 Jaeger 的“断链”处找到缺失的 Span,反向定位那个服务的接入配置。

可观测性不是装个软件,而是一套从标准、采集、存储到消费的完整飞轮。用快递思维复盘:面单规范让数据说得清,查询网站让数据看得见,科学采样让成本控得住,链路覆盖率让体系一直转得动——这就是你交付给业务的可观测性体系。

Ptrace=NcompleteNtotal×100%\displaystyle P_{\text{trace}} = \frac{N_{\text{complete}}}{N_{\text{total}}} \times 100\%
通俗解释:链路完整率 = 成功走完全程的包裹 ÷ 总包裹数,是衡量可观测性体系是否真正落地的标尺。
  1. N_complete 完整链路数从入口到出口 Span 完整、上下文未断裂的请求数量
    NcompleteN_{\text{complete}}
  2. N_total 总请求数观察周期内的全部请求数量
    NtotalN_{\text{total}}
  3. 相除与百分比比值越接近 100%,说明埋点和上下文传播越可靠,体系越健康
    NcompleteNtotal×100%\frac{N_{\text{complete}}}{N_{\text{total}}} \times 100\%

AI 应用场景

微服务故障快速定位

阶段:故障排查与根因分析

解决的问题:传统方式是告警响了以后,登录每一台服务器翻日志、比对时间线,一次线上事故定位往往要几小时。接入 OpenTelemetry 统一埋点与 Jaeger 后,以 TraceID 为“快递单号”一键检索,从瀑布图直接看到错误发生在哪个服务、哪个 Span、耗时多少,把 MTTR 从小时级降到分钟级。

具体案例:电商大促期间用户反馈“下单后一直转圈”,SRE 在 Jaeger 搜索该时段异常 Trace,发现 98% 的慢请求都卡在“库存服务扣减”Span,进一步下钻是 Redis 热 key 导致,十分钟内完成限流与缓存重建。

核心链路性能瓶颈分析

阶段:性能优化与容量评估

解决的问题:传统压测只能看到整体 TPS 和平均延迟,无法回答“慢在哪”。通过追踪瀑布图可以看到每个 Span 的耗时占比:网关耗时多少、下游服务等待多少、数据库查询多少,从而精准定位瓶颈是网络、代码还是存储。

具体案例:某支付团队优化下单接口 P99 延迟,靠 Jaeger 的 Trace 对比功能,发现 40% 时间消耗在“订单服务调用支付网关”的等待上,于是引入异步回调 + 并行调用,P99 从 800ms 降到 200ms。

大促容量评估与发布验收

阶段:容量规划与发布验证

解决的问题:传统容量评估靠压测单机再估算,忽略了服务间依赖放大效应。基于采样后的全链路数据,可以统计每个服务承接的 QPS 与延迟分布,倒推各服务所需实例数;发布新版本时,也能对比新旧版本的 Span 耗时与错误率,用数据验收上线质量。

具体案例:某出行平台在春节前用 Jaeger 历史数据做容量演练,发现“司机调度服务”承接了 70% 的链路流量且依赖外部地图 API,提前扩容并配置降级开关,大促期间全程零事故。

Python 演示

python76 行
import random  class Span:    def __init__(self, name, service, parent_id=None, start_ms=0):        self.name = name        self.service = service        self.parent_id = parent_id        self.start_ms = start_ms        self.duration_ms = 0     def __repr__(self):        return "<%s %s start=%dms dur=%dms>" % (            self.service, self.name, self.start_ms, self.duration_ms)  class Trace:    def __init__(self, trace_id, sampled=True):        self.trace_id = trace_id        self.sampled = sampled        self.spans = []     def add_span(self, name, service, parent, start_ms):        span = Span(name, service, parent_id=id(parent), start_ms=start_ms)        self.spans.append(span)        return span  class SimpleTracer:    def __init__(self, sample_rate=1.0):        self.sample_rate = sample_rate     def start_trace(self, trace_id):        sampled = random.random() < self.sample_rate        return Trace(trace_id, sampled)  def simulate_order_flow(trace_id, sample_rate=1.0):    tracer = SimpleTracer(sample_rate)    trace = tracer.start_trace(trace_id)    if not trace.sampled:        return None     root = trace.add_span("handle-request", "gateway", None, 0)    order = trace.add_span("place-order", "order", root, 2)    pay = trace.add_span("deduct-payment", "payment", order, 6)    stock = trace.add_span("reduce-stock", "stock", order, 9)    root.duration_ms = 14    order.duration_ms = 11    pay.duration_ms = 5    stock.duration_ms = 4    return trace  def render_waterfall(trace):    lines = ["Trace %s sampled=%s" % (trace.trace_id, trace.sampled)]    for span in sorted(trace.spans, key=lambda s: s.start_ms):        bar = "#" * span.duration_ms        lines.append("  %-18s %4dms %s" % (            span.service + "." + span.name, span.duration_ms, bar))    return "\n".join(lines)  def main():    for index in range(5):        trace = simulate_order_flow("trace-" + str(10000 + index), sample_rate=0.6)        if trace is None:            print("trace-%d dropped by head sampling" % (10000 + index))            continue        print(render_waterfall(trace))        root = trace.spans[0]        print("  total latency(root span): %dms" % root.duration_ms)  if __name__ == "__main__":    main()
目的:用 Python 模拟 OpenTelemetry 头部采样与 Trace/Span 数据模型,直观展示链路如何被采样丢弃、以及瀑布图如何呈现。
  1. sampled = random.random() < self.sample_rate头部采样:请求还没发出,先抛硬币决定这条 Trace 留不留档
  2. trace = tracer.start_trace(trace_id)给请求“贴快递单号”,返回带采样标记的 Trace 对象
  3. order = trace.add_span("place-order", "order", root, 2)每经过一个服务就登记一个 Span,用 parent 建立父子关系
  4. bar = "#" * span.duration_ms用井号串长度可视化耗时,渲染出微型瀑布图
  5. if __name__ == "__main__":可执行入口,直接运行即可观察到部分 Trace 被采样丢弃

常见误区

误区“我们把 Jaeger 部署上线了,可界面上什么链路都没有,是不是 Jaeger 有问题?” 初学者常以为可观测性后端是“装上就生效”的黑盒。

正解Jaeger 只是“物流查询网站”,它本身不产生数据。真正关键的是应用侧埋点:引入 OpenTelemetry SDK、为每个请求创建 Trace、在跨服务调用时通过 Header 传播 SpanContext。没有埋点和上下文传播,后端什么都收不到,就像快递分拣口没人扫码,查询网站自然是空白。

为何常见:监控类组件(如 Prometheus 拉取指标)确实接近“装上就采”,但链路数据必须由应用主动上报,两者原理完全不同,容易混淆。

误区“为了不漏掉任何一个慢请求,我把采样率设成 100%,这样最保险。” 这是对数据完整性的执念。

正解100% 采样意味着所有请求都写入存储,数据量随流量线性膨胀,存储与查询成本快速失控,查询变慢反而拖累排障。更合理的是:正常流量用 5%~10% 头部采样,异常或超时请求通过尾部采样保证 100% 保留,兼顾成本与关键数据。

为何常见:人对“丢失数据”天然恐惧,却很少意识到全量采集的成本远高于其边际收益。

误区“我在日志里打了 trace_id,这不就是分布式追踪吗?” 很多从日志排查入门的工程师这样理解。

正解给日志加 ID 只能靠检索把散落记录拼起来,无法自动还原调用拓扑与耗时。真正的链路追踪要求每个服务产出结构化 Span(服务名、开始时间、耗时、状态、父子关系),并用 SpanContext 把因果链串成树,Jaeger 才能画出瀑布图。前者像手抄本,后者是自动生成的物流轨迹树。

为何常见:两者都涉及“请求标识”,但忽略了 Span 的树状结构与时间属性,把“相关 ID”误当成“完整链路”。

误区“既然用了 OpenTelemetry,为什么还要装 Jaeger?这俩不是重复的吗?” 名字都带可观测性,让人误以为功能重叠。

正解二者根本不在同一层。OpenTelemetry 是“生产标准”,负责在应用里埋点、生成统一格式数据并导出;Jaeger 是“消费端”,负责追踪数据的存储、检索与可视化。用快递比喻:OpenTelemetry 是国标面单,Jaeger 是物流查询网站,面单写好了总得有网站展示。

为何常见:两者都能处理 Trace 数据,初学者容易混淆“数据采集层”与“分析展示层”的边界。

误区“我在每个服务里引入了 OpenTelemetry 的依赖,为什么链路还是不完整?” 接入依赖后依然大量断链。

正解引入 SDK 只是拿到“纸和笔”,还需要真正“写字”:HTTP 框架要配置拦截器自动注入,消息队列要透传 SpanContext,关键业务逻辑要手动创建 Span。常见做法是启动时用 Agent 自动注入框架适配器,再对核心方法手动打点,最后用链路完整率验证接入效果。

为何常见:SDK 名称里带“自动”字样容易让人高估,实际上自动注入依赖框架适配器,并非零配置。

面试题

Q1. 分布式追踪中 Trace(追踪)的核心作用是什么?

单选题 · easy

  • A.记录单台服务器的运行日志
  • B.关联一次请求在所有服务的完整路径← 正确答案
  • C.采集基础设施的 CPU 与内存指标
  • D.提升微服务接口的响应速度

答案:B。追踪的核心是还原一次请求的完整路径,就像用快递单号查询包裹全程。A 是日志做的事,C 是指标做的事,D 是优化目标而非追踪本身,故选 B。

Q2. 以下哪个是 Span 的关键属性?

单选题 · easy

  • A.服务器 IP、CPU 使用率
  • B.服务名、开始时间、耗时、状态← 正确答案
  • C.磁盘 IO 与网络带宽大小
  • D.数据库连接池的配置

答案:B。Span 是链路中的单个服务调用单元,需要服务名、开始时间、耗时与状态,足以画瀑布图。A 和 C 属于基础设施指标,D 属于数据层配置,都不属于 Span 的必要属性,就像物流单上的每一行“到达记录”只记录站点、时间和是否异常。

Q3. 关于 OpenTelemetry 与 Jaeger 的关系,下列说法正确的是?

单选题 · mid

  • A.两者功能重复,实际只能二选一
  • B.前者负责采集规范,后者是分析后端← 正确答案
  • C.Jaeger 定义协议,OpenTelemetry 负责存储
  • D.两者都是编程语言的运行时框架

答案:B。OpenTelemetry 负责在应用里统一埋点并导出数据(采集规范),Jaeger 负责接收、存储和可视化(分析后端)。好比国标面单与物流查询网站的关系,C 把两者说反了,A 和 D 均不符合实际。

Q4. 头部采样与尾部采样的本质区别是什么?

单选题 · mid

  • A.采样位置不同,客户端与服务端差异
  • B.头部入口决策,尾部按结果决策← 正确答案
  • C.头部采样成本高,尾部采样成本低
  • D.尾部采样只保留全部失败请求

答案:B。头部采样在请求刚发起时用随机概率决定是否采样,尾部采样等请求结束按结果(错误、超时)决策。类比出发前随机抽检包裹与到达后对问题件全查。A 把区别简单归为位置,C 说反了成本,D 绝对化且不完整。

Q5. W3C TraceContext 的 traceparent 请求头主要作用是什么?

单选题 · mid

  • A.传递调用链的 TraceID 与 SpanID← 正确答案
  • B.加密跨服务传递的业务数据
  • C.替代日志采集的传输通道
  • D.压缩跨服务调用的报文体积

答案:A。W3C TraceContext 定义了携带 TraceID、SpanID 和采样标记的请求头,服务间靠它恢复上下文,像接力棒必须传递给下一棒。B 是加密业务数据,C 和 D 都与链路追踪无关。

Q6. 某系统每秒 1 万请求,采样率 0.1,单条 Trace 约 1KB,一天存储量接近?

单选题 · hard

  • A.86GB← 正确答案
  • B.8.6GB
  • C.860GB
  • D.8.6TB

答案:A。按公式计算:1 万 QPS × 0.1 = 1000 条/秒,× 86400 秒 = 8640 万条/天,每条 1KB 约 86.4GB,故选 A。这正体现“采样率降十倍,存储成本也降十倍”的线性关系。

Q7. 在分布式追踪中,SpanContext 需要跨服务传递哪些关键信息?

多选题 · mid

  • A.客户端 IP 地址与用户 ID
  • B.TraceID 和当前 SpanID← 正确答案
  • C.采样标记与链路状态← 正确答案
  • D.服务所在物理机 IP 与端口

答案:BC。SpanContext 里装着 TraceID(快递单号)、当前 SpanID(当前站点)和采样标记(是否留档),服务间通过请求头传递这三者。A 和 D 属于业务或网络信息,不是 SpanContext 的必需字段。

Q8. 为什么不能直接把追踪数据写入业务数据库?

多选题 · hard

  • A.高并发写入会拖垮业务数据库← 正确答案
  • B.检索模式与事务存储不匹配← 正确答案
  • C.业务数据库版本一定太低
  • D.追踪数据不需要持久化存储

答案:AB。追踪数据量每秒数千条且写入频繁,直接写业务库会拖垮在线事务;链路检索按 TraceID 深度扫描,与事务库的索引结构不匹配。就好比快递面单不会塞进普通文件柜,而要进专门的档案库。C 绝对化错误,D 与事实相反。

Q9. OpenTelemetry 提供的核心组件包含哪些?

多选题 · mid

  • A.统一的 API 与多语言 SDK← 正确答案
  • B.OTLP 标准导出协议← 正确答案
  • C.追踪可视化的大屏界面
  • D.业务数据仓库解决方案

答案:AB。OpenTelemetry 的构成是统一 API/SDK 和 OTLP 导出协议,外加丰富的框架适配库。C 的 UI 属于 Jaeger 等分析端,D 属于业务存储范畴,都不在 OTel 核心范围内。

Q10. 以下哪项属于可观测性的三大支柱之一?

单选题 · easy

  • A.服务熔断与限流策略
  • B.分布式链路追踪技术← 正确答案
  • C.容器编排与调度平台
  • D.数据库读写分离方案

答案:B。可观测性三大支柱是日志、指标与链路追踪,B 正确。A 属于韧性设计,C 属于基础设施,D 属于存储优化,均不属于可观测性支柱。