题干与适用场景
一个 HTTP 请求发布消息后由异步消费者处理,你如何传播 Trace Context 并保证重试、批处理和跨租户安全?这道题适用于后端、可观测性和消息系统面试。重点是跨执行边界保持因果关联,同时避免把请求上下文当成永久授权或业务数据。
面试官考察点
- 是否理解
traceparent、可选的tracestate与传播器的职责边界。 - 是否能区分生产者、消息处理和每次重试的 span 与 parent 关系。
- 是否处理批量消息、延迟消费、死信、采样和上下文过期。
- 是否避免传播敏感 baggage、跨租户数据或可伪造的信任信息。
回答前需要澄清的问题
先确认传输协议、消息是否持久化、是否支持批处理和重试,以及消费者是否可能跨服务或跨信任域。明确要关联的是一次业务操作、一个消息,还是一批消息。再问采样策略、追踪系统保留期、租户隔离和是否允许外部生产者注入上下文。最后确认失败消息如何进入死信以及人工重放是否创建新的追踪分支。
30 秒回答框架
“入口服务提取并验证传播头,在发布消息时注入最小的 trace context。消费者从消息载荷提取它,创建独立的消费 span,并把每次重试、批处理和死信表示为清晰的子关系。跨信任域只接受受控字段,敏感 baggage 不出域;采样和过期策略由平台统一定义,重放使用新的 trace 标识并保留原始关联。”
分步骤深入解答
- 定义边界:把 HTTP 入站、消息发布、消息传输和消费处理建模为不同执行单元,约定谁负责注入与提取。
- 传播载体:优先使用标准传播格式放在消息 headers 或受控 metadata;不要把完整请求、身份令牌和任意 baggage 复制进消息。
- span 关系:发布者创建 producer span,消费者创建 consumer span;批处理时记录消息关联,避免把整批误标成单一请求。
- 重试与死信:每次尝试有独立 span 和 attempt 属性,保留原始消息关联;死信与人工重放创建新分支,避免伪造历史时间线。
- 安全与治理:限制跨域注入、清洗用户可控字段、隔离租户标签,设置采样、保留和上下文大小上限。
高质量示范回答
我会把传播分成标准上下文、业务关联和安全边界三层。HTTP 入口只提取符合格式的传播头,验证版本与长度后创建服务器 span。发布消息时,生产者 span 将最小 trace context 注入消息 metadata,同时另存不可变的业务事件 ID,二者用途不同。消费者提取 metadata,创建 consumer span;如果一条消息触发多个下游操作,每个操作建立自己的子 span。批处理不把所有消息强行合并成一个 parent,而是记录批次 span 和有限的消息链接。每次重试增加 attempt 和退避信息,仍保留原始事件 ID;进入死信后,人工重放创建新的 trace,链接回原始 trace,避免把新执行伪装成旧执行。跨租户或外部生产者的 baggage 默认丢弃,只允许平台批准的低敏字段。最后用上下文大小、提取失败率、消息到消费的关联率、重试可见性和跨租户泄露测试验证方案。
常见错误
- 把 trace ID 当作认证凭证或业务幂等键。
- 将完整 HTTP headers、用户输入或 token 原样写入持久化消息。
- 把批量消费和多次重试都挂在同一个 span 上,导致时序失真。
- 死信重放复用旧 trace,无法区分原始失败与新尝试。
- 只讲 SDK 调用,不说明跨信任域、采样、保留和上下文大小治理。
追问及应对
消息被重试十次,应该有几个 span?
至少为每次实际处理尝试建立可区分的 span,并通过 attempt、事件 ID 和链接关联原始生产操作。这样既能比较每次延迟,也不会把十次执行误报为一次。
批量消息如何建立 parent?
创建代表批次的消费 span,再为需要分析的消息建立链接或子 span。不要任意选择第一条消息作为整批 parent;采样不足时保留批次级统计和关联 ID。
外部客户可以注入 tracestate 吗?
可以接收符合协议的字段,但不能默认信任其内容。跨信任域要限制长度、键集合和转发范围,清除敏感或高基数字段,并避免把它用于授权决策。
人工重放死信时如何保持可追踪?
为重放创建新的 trace 和执行 span,记录原始消息 ID、操作者、原因和重放批次。新旧链路通过受控链接关联,保留原始失败的不可变记录。