LabHub
学习 学习路径 课程

OTCA — OpenTelemetry 认证助理

传播断掉的四个地方,以及各自的处方

在 LabHub 中继续学习

一句话总结

构成 trace 这棵树的唯一机制是上下文传播。调用方把当前 trace ID 和 span ID 放入 W3C traceparent 头,接收方读取后作为自己 span 的父级。链条几乎总在固定位置断裂:线程池、消息队列、后台任务,以及删除头部的中间层。

概念图: 上下文传播 · 解析 traceparent · 断点 1——线程池与 executor。 · 断点 2——消息队列。

为什么需要它

打开一条生产 trace,发现请求经过六个服务,却只有两个 SERVER span。此时添加手工 span 毫无意义;本周真正要做的是恢复其余四处传播。

工作原理

解析 traceparent

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
             --  --------------------------------  ----------------  --
             |   |                                 |                 |
             |   |                                 |                 +-- flags: 01 이면 샘플링됨, 00 이면 저장 안 됨
             |   |                                 +-- parent-id: 16자리 16진수(8바이트). 호출 단계마다 바뀐다
             |   +-- trace-id: 32자리 16진수(16바이트). 요청 전체에서 동일하다
             +-- version: 현재 00

tracestate 是携带厂商附加信息的伴随头;baggage 是跨服务边界传递用户自定义键值的独立规范。baggage 不是 span 属性,因此不会自动存储。

断点 1——线程池与 executor。 是否传播取决于运行时和所用 API。没有计量 wrapper 的普通 ThreadPoolExecutor.submit,在本实验环境中不会自动传递调用方上下文。Python 3.12 的 asyncio Task 和 to_thread 会复制 Context,但要区分协程创建与实际调度时点。缺少自动支持的边界,要在调用方捕获上下文,在 worker 中激活,执行后还要恢复 worker 原有状态。后续上下文边界实验将用两个订单和复用 worker 验证。

断点 2——消息队列。 队列既是进程边界也是时间边界。上下文不会像 HTTP 头一样自动流动,producer 必须注入消息头,consumer 再提取。关键设计判断是:批量 consumer 应使用 Link,而非父子关系。 一次处理 100 条消息有 100 个父级,但 span 只能有一个父级。等待时间很长时,即使只有一条消息也应考虑 Link;强行使用父子关系会把整个 trace 的持续时间拉长到包含队列等待,令后端难以处理。

断点 3——后台任务与调度器。 cron 或后台任务没有父级。常见错误是强行接上请求上下文。请求已经返回结束,却挂上一个 30 秒子 span,会污染请求延迟统计。正确方法是从新 root trace 开始,并用 Link 保留来源请求

断点 4——删除头部的中间层。 代理、WAF、API gateway 或 CDN 以白名单过滤头部时,traceparent 会悄然消失。日志无记录,症状只是“从 gateway 后 trace 重新开始”。确认允许列表包含 traceparenttracestatebaggage;若混有使用 B3 头的旧系统,则配置多个 propagator。

资源属性与 span 属性的边界也是考试内容。

分类 对象 示例
资源属性 产生遥测的主体 service.name, k8s.pod.name, host.name
span 属性 一次工作 http.route, db.system, cart.item_count

span 属性的高基数与指标不同,并不会直接造成时间序列爆炸。订单 ID、用户 ID、查询参数正是 trace 需要承载的值,否则 trace 只是无法筛选的图片。问题出现在两处:把 span 转换成指标时(spanmetrics dimensions 中的属性会直接成为指标标签),以及 span 本身的大小

service.instance.id 基数虽高,却是资源属性,在 trace 和日志中没有问题。但若提升为指标标签,时间序列会按 Pod 数量成倍增长,因此通常只在 Collector 的指标 pipeline 中删除。

现场会遇到的情况

确认传播是否存活的最低成本方法,是模拟 gateway:把已知 trace ID 手工放入 traceparent 头发送请求,然后在后端按该 ID 查询,查看 span 是否接到其下。若没有,就是上述四种原因之一。

“计量完成”也应使用检查清单:选择一个生产请求,参与服务数是否等于 SERVER span 数;root span 持续时间是否与 gateway 访问日志响应时间一致;最大 self time 是否小于总时长 20%;能否用 trace ID 搜到日志;span 名称前 20 是否为路由模板而非 ID;跨队列工作是否以一条 trace 或 Link 相连。最后一项——杀死一次 Collector 后,应用错误率和延迟是否不受影响——只有实际测试才知道。

下一项检查要看什么

本模块是概念模块,没有实验。测验确认 traceparent 字段和 Link 的判断标准后,最后一个模块将进入采样策略与缓冲区内存计算。