feign拦截器应在apply()方法内实时获取traceid并注入header,使用tracer.currentspan().context().traceid()并判空;全局拦截器仅对@feignclient生效,直连url会绕过;需确保header名一致、版本兼容及网关透传。

Feign 拦截器里怎么拿到并注入 TraceId
Spring Cloud Sleuth(或 Micrometer Tracing)会在 ThreadLocal 中维护当前请求的 TraceContext,而 Feign 拦截器执行时仍处于原线程(同步调用场景),所以可以直接读取。关键不是“怎么传”,而是“什么时候读”——必须在 RequestTemplate 构建完成前注入,否则 header 写不进去。
常见错误是试图在拦截器外提前取 traceId 并缓存,结果跨线程或异步调用时拿到空值;正确做法是在 apply(RequestTemplate template) 方法体内实时调用 tracer.currentSpan() 或 tracer.currentTraceContext().get()。
- 使用
Tracerbean(Sleuth 3.x / Micrometer Tracing):tracer.currentSpan().context().traceId() - 兼容老版本 Sleuth 2.x:用
Tracing+CurrentTraceContext组合获取 - 务必判空:
if (span != null),避免未埋点请求(如健康检查)抛 NPE
为什么 @Bean 注入的 FeignClient 不自动生效拦截器
Feign 的拦截器分两类:全局默认拦截器(feign.RequestInterceptor 类型的 Bean)和客户端级拦截器(通过 @FeignClient(configuration = ...) 指定)。前者只对声明了 @FeignClient 的接口生效,但不会作用于 @LoadBalanced RestTemplate 或手动构建的 Feign.Builder 实例。
典型坑点:你在主配置类里加了 @Bean public RequestInterceptor traceIdInterceptor(Tracer tracer),但某个 @FeignClient(name = "user-service") 接口没走负载均衡(比如直连 url = "http://127.0.0.1:8081"),此时 Ribbon/ServiceDiscovery 不参与,Feign 默认 client 也不会加载 Spring 上下文里的拦截器 Bean。
- 确认
@FeignClient没写死url,否则绕过 Spring Cloud 的自动装配逻辑 - 检查是否引入了
spring-cloud-starter-openfeign而非仅openfeign-core - 若需强制启用,可在 client 配置类中显式声明:
@Configuration public class FeignConfig { @Bean public RequestInterceptor traceIdInterceptor() { ... } },再通过configuration = FeignConfig.class关联
TraceId 透传失败的三个高频现象及定位方式
不是代码没写,而是链路断在中间某环。最常出问题的是 header 名称不一致、大小写敏感、或被网关/代理过滤。
现象一:X-B3-TraceId 在服务 A 发出时存在,服务 B 的日志里却为空 —— 很可能 B 没引入 Sleuth 依赖,或用了旧版(Sleuth 2.x 默认用 X-B3-TraceId,3.x 默认用 traceparent,两者不兼容)。
现象二:本地调试正常,上 K8s 后丢失 —— Istio/Envoy 默认不透传自定义 header,需在 VirtualService 中显式配置 headers: request: set: 或开启 tracing 策略。
现象三:Feign 调用成功,但 Zipkin 页面看不到父子关系 —— 可能漏传了 X-B3-SpanId 和 X-B3-ParentSpanId,仅传 TraceId 不够,必须完整传递 span 上下文字段。
- 验证方式:在 Feign 拦截器里加日志,打印
template.headers()确认 header 是否写入 - 抓包看实际 HTTP 请求 header(如用
tcpdump或 Spring 的LoggingFeignLogger) - 对比上下游服务的 tracing starter 版本和配置项
spring.sleuth.baggage.remote-fields
要不要在 Feign 拦截器里处理采样逻辑
不需要,也不应该。TraceId 透传和采样是两个正交职责:前者确保上下文可传递,后者决定是否上报数据。Sleuth/Micrometer 的采样决策发生在 Tracer.nextSpan() 阶段,基于当前 span 的 parent、name、tag 等动态计算,与 Feign 拦截器无关。
强行在拦截器里判断“是否采样再决定是否传 header”,会导致链路断裂(下游无法创建 child span),且破坏分布式追踪语义。真正要控制的是 spring.sleuth.sampler.probability(旧版)或 management.tracing.sampling.probability(新版本)这类全局配置。
- 唯一例外:某些灰度场景需按 header 值强制采样,此时应在
SamplerBean 中实现自定义逻辑,而非拦截器 - Feign 拦截器只做一件事:把当前 span context 的标准字段(
traceparent或X-B3-*)无损复制到 outgoing request header - 如果用了 OpenTelemetry,对应字段是
traceparent和tracestate,header 名称和格式完全不同,不能混用
traceparent,Sentinel 的降级规则可能误判带 trace header 的请求,Kafka binder 默认不传播 baggage。这些都不是 Feign 拦截器能解决的,得逐个对齐。










