LabHub
学习 学习路径 课程

OTCA — OpenTelemetry 认证助理

断了,span 还是留着

在 LabHub 中继续学习

一句话总结

即使传播中断,各服务自己的 span 仍会完整保留。因此,症状不是“追踪失效”,而是“看不到请求后半段”。

流程图: 没有请求往来 · 各服务的 span 仍完好存在 · “为什么看不到这个请求的后半段?” · 无父 span 的比例

为什么必须有真实请求往来

前面的模块讲过传播和 Collector,但那些实验环境中没有请求往来,所以根本不会发生传播中断。尽管这是 OTCA 最常考的部分。

中断是安静的

传播中断后,各服务的 span 仍完好存在。打开页面能看到 trace,名称和时间也正常,只是没有连成一条。

因此症状不是“追踪失效”,而是**“为什么看不到这个请求的后半段?”** 原因更难查找。

1. 라이브러리가 헤더를 안 붙인다      직접 만든 HTTP 클라이언트가 흔하다
2. 프록시가 헤더를 지운다             허용 목록에 traceparent 가 없다
3. 큐를 건너갈 때 잃는다              메시지 본문에 넣어 손으로 이어야 한다
4. 스레드·비동기 경계에서 끊긴다      컨텍스트가 따라가지 않는다

后两种尤其安静。HTTP 还能检查 header,队列与异步边界则没有明显可查看的位置。

要发现它,必须把无父 span 的比例做成指标。某个服务持续产生 root span,说明传播在它前面中断。

span 已到达,页面却看不到时

制作实验时确实遇到过:Collector 日志显示收到 span,页面却空无一物。

原因是时间戳。 填入过小数值后,数据被索引到 1970 年,默认查询范围(最近)找不到。Jaeger 查询 API 不给时间范围时也会返回空结果。

手工连接跨队列传播

HTTP 的计量库会添加 header,消息队列却没人代劳。发布时要把上下文写入消息,消费时再提取。

from opentelemetry import propagate, trace

# 발행 쪽 — 현재 컨텍스트를 헤더 맵에 주입한다
def publish(body):
    carrier = {}
    propagate.inject(carrier)                  # traceparent 가 여기 들어간다
    queue.send({"body": body, "otel": carrier})

# 소비 쪽 — 꺼낸 컨텍스트를 부모로 삼는다
def consume(msg):
    ctx = propagate.extract(msg.get("otel") or {})
    with tracer.start_as_current_span("handle", context=ctx,
                                      kind=trace.SpanKind.CONSUMER):
        handle(msg["body"])

若没有向 start_as_current_span 传递 context=,就会生成新的 root span,trace 在此处中断。这是无父 span 增加最常见的原因。

异步边界还应正确设置 kind。标记 PRODUCERCONSUMER 后,UI 会以不同方式绘制队列区间,队列等待时间也能看见。

基数决定成本

span 属性中放什么,会左右存储成本和查询速度。

可以放入 不应放入
http.route (/users/{id}) 完整 http.target (/users/48213)
db.system, db.operation 完整 SQL(含参数)
messaging.destination 消息正文
租户 ID(数百个值) 用户 ID(数百万个值)

直接放完整路径,属性值会按请求数增长,索引随之爆炸。应使用按模板归一化的路由。个人信息在任何情况下都不应放入属性,因为 trace 通常保留策略宽松且可被多人查看。

先计量什么

试图一次计量全部内容,会根本无法开始。顺序如下。

  1. 入口——HTTP 服务器和队列 consumer。只有这些也能回答“哪个请求慢”。
  2. 外部调用——HTTP 客户端、DB、缓存。到这里可回答“在哪里慢”。
  3. 应用内部——只选择繁重计算区间。每个函数都建 span 只会增加成本并让界面难读。

第 1、2 项大多可通过自动计量获得,手工工作从第 3 项开始。

实务中真正重要的事

把无父 span 比例作为指标。 这是人发现传播断裂的实际唯一方法。某服务持续创建 root span,就能定位中断发生在其前方。

队列与异步边界必须手工连接。 HTTP 计量库会添加 header,但把上下文写入消息再提取,没人会代做。有队列的区间,传播必须进入设计阶段。

span 不可见时先检查时间戳。 若填入的不是纳秒值,数据会索引到 1970 年,在默认查询范围中永远找不到。Collector 日志显示“已接收”而页面为空时,几乎就是这个原因。

下一实验将在真正的 Collector 和真实请求上亲自验证这些现象。