用 $request_id 做流量染色可行,但必须由入口网关(如 Nginx)生成并透传,下游服务主动提取注入 tracing context;否则链路断裂,分布式追踪失效。

直接说结论:用 $request_id 做流量染色可行,但必须确保它在入口层生成、全程透传、不被覆盖,且下游服务主动提取并注入到日志/HTTP头/SDK上下文中;否则链路会断裂,分布式追踪形同虚设。
入口网关必须生成并透传 $request_id
Nginx 或 API 网关是唯一可信的 $request_id 生成点。不能依赖后端服务自己生成,否则同一请求在多个实例中会拿到不同 ID,染色失效。
- 在 Nginx 中用
map+random或lua-resty-random生成 UUID(避免用$pid$microsec这类低熵值组合) - 务必通过
proxy_set_header X-Request-ID $request_id;显式透传,不能只靠默认继承 - 若上游已带
X-Request-ID,需加判断逻辑:仅当 header 为空时才生成新 ID,避免覆盖真实链路 ID
Go/Java/Python 服务必须主动读取并绑定到 tracing context
语言 SDK(如 OpenTelemetry)默认不识别 X-Request-ID,也不会自动把它设为 trace ID。不手动桥接,$request_id 就只是日志里一个字段,进不了 Jaeger / Zipkin。
- Go(gin):在中间件中用
c.GetHeader("X-Request-ID")拿到值,再调用otel.GetTextMapPropagator().Inject(...)注入 carrier - Java(Spring Boot):用
ServletFilter提取 header,再通过OpenTelemetry.getPropagators().getTextMapPropagator().inject(...)注入 - Python(Flask):用
request.headers.get("X-Request-ID"),配合trace.set_span_in_context()和自定义TextMapPropagator - 关键陷阱:不要把
$request_id直接赋给SpanContext.trace_id——OpenTelemetry 要求 trace_id 是 16 字节二进制或 32 位 hex 字符串,UUID 需做uuid.UUID(...).bytes转换
异步调用和 RPC 场景下容易丢失 $request_id
HTTP 请求能靠 header 透传,但 Kafka 消息、定时任务、线程池里的子任务不会自动携带。这里不是“透传失败”,而是根本没设计透传路径。
- Kafka 生产者发消息前,必须手动把当前
$request_id写入headers(不是 payload),消费者侧再从中提取并重建 context - 使用
CompletableFuture或asyncio.create_task()时,需显式将当前 tracing context 作为参数传入,不能依赖线程/协程局部变量 - gRPC 场景下,
$request_id应放入metadata,服务端用grpc.ServerInterceptor提取并激活 span - 注意:Logback / Log4j 的 MDC 只在线程内有效,跨线程(如
ExecutorService.submit())必须手动MDC.copyInto()
真正难的不是生成一个 ID,而是让这个 ID 在所有通信边界(HTTP/Kafka/gRPC/DB 调用/线程切换)上不被丢、不被错、不被混淆。一旦某个环节漏掉提取或注入,整条链路就断成两截,你看到的只是孤立的 span,不是 trace。










