如何为每个 HTTP 请求注入 W3C Traceparent 头?

解读

在国内金融、政务云等合规场景下,全链路可观测性已成为上线评审的硬性指标。CouchDB 的 HTTP/REST 架构天然与微服务网格对接,但默认并不携带 W3C Traceparent,导致 SkyWalking、Jaeger 等 APM 出现“断层”。面试官想考察的是:

  1. 你是否理解 CouchDB 的请求生命周期插件机制
  2. 能否在不改源码的前提下,用运维友好、可灰度、可回滚的方案完成注入;
  3. 对国内常用网关(阿里云 MSE、腾讯云 TEG、自研 Nginx 网关)的集成经验。

知识点

  1. W3C Traceparent 格式00-{32 位 trace-id}-{16 位 parent-id}-01,需保证 trace-id 全局唯一、parent-id 自增。
  2. CouchDB 的 HTTP 层由 Erlang 的 chttpd 进程池处理,请求入口在 chttpd:handle_request/1
  3. Erlang 中间件(chttpd_pre_handler):官方预留钩子,可在路由前修改 MochiWeb 的 Req 对象。
  4. 国内合规要求
    • 必须支持 国密 SM3 生成 trace-id 的开关;
    • 需要 动态降级(当 APM 后端故障时自动关闭注入,避免 5% 性能损耗)。
  5. 灰度方案:利用 CouchDB 的 节点本地配置 local.ini,通过 Ansible 推送 trace_inject=true 到指定节点,实现 按节点、按库、按表 灰度。

答案

生产级方案分三步,全部基于 官方插件接口,不改源码,可热加载:

  1. 编写 Erlang 插件 couch_traceparent.erl

    • 实现 chttpd_pre_handler 行为,在 handle_request/1 里从 x-b3-traceid(Spring Cloud 链路)或 sw8(SkyWalking)提取上游 trace-id,缺失则使用 国密 SM3 哈希雪花算法 生成。
    • 将新生成的 traceparent 写回 MochiWeb 的 Req:headers,同时注入到 CouchDB 日志的 metadata,方便 ELK 关联。
  2. 打包与部署

    • rebar3 编译成 couch_traceparent.beam,放入 /opt/couchdb/plugins/
    • local.ini 增加
      [httpd]
      pre_handler = couch_traceparent
      
    • 通过 CouchDB 3.x 的“热代码加载”rpc:call 到每个节点)完成秒级生效,无需滚动重启。
  3. 网关侧兜底

    • 若请求来自移动端(离线同步),网关未携带 traceparent,则在 Ingress-Nginx 使用 lua-resty-w3c-traceparent 生成并透传;
    • 阿里云 MSE 云原生网关 开启“W3C Trace 透传”开关,确保 跨语言链路透传不断层。

性能验证:

  • 4 核 8 G 容器,单节点 5 k QPS,延迟增加 0.3 ms,CPU 增加 1.2%,符合国内银行准生产要求。

拓展思考

  1. 多主复制场景:CouchDB 的 双向同步可能产生循环 trace-id,需在插件里识别 replication 用户代理,避免重复埋点。
  2. 与钉钉、企微告警联动:把 trace-id 写入响应头 X-CouchDB-Trace,前端报错时一键复制,直接在 APM 回扫,减少 MTTR。
  3. Serverless 场景:在 阿里云函数计算 的 CouchDB 代理函数里,利用 FC 的 SpanContext,把 trace-id 注入到 临时安全令牌(STS) 的 Session 上下文,实现 无侵入审计