异步的feign请求报错:No thread-bound request found
接上一篇:https://blog.csdn.net/JFENG14/article/details/153473176?spm=1011.2415.3001.5331
问题:添加FeignHeaderConfig和HystrixConfig后,异步的feign请求报错:No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
解决方法:
-
非 Web 场景下不要尝试读取请求头
FeignHeaderConfig
已经在attributes == null
时return;
,这是正确做法。
然而,Hystrix 线程里 attributes 不再为 null(被你包装过了),但实际请求已经结束(例如异步定时任务),这时HttpServletRequest
对象已失效,再用它拿 Header 会触发上述异常。 -
把“需要透传的 Header”提前摘出来,放到线程变量里
用 InheritableThreadLocal(或阿里推荐的 TransmittableThreadLocal)在主线程就把 Header 快照下来;
后续任何线程(Hystrix、异步、Feign 重试)都读这份“快照”,而不再去碰RequestContextHolder
。
1.定义一个快照容器
public final class RequestHeaderSnapshot {private static final InheritableThreadLocal<Map<String, String>> SNAPSHOT =new InheritableThreadLocal<>();/** Web 请求入口时调用:把指定 Header 快照下来 */public static void capture(Set<String> headerNames) {ServletRequestAttributes attr =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attr == null) {SNAPSHOT.remove();return;}HttpServletRequest req = attr.getRequest();Map<String, String> map = new HashMap<>();headerNames.forEach(h -> {String v = req.getHeader(h);if (StringUtils.isNotBlank(v)) {map.put(h, v);}});SNAPSHOT.set(map);}/** 任何地方获取快照 */public static Map<String, String> get() {return SNAPSHOT.get() == null ? Collections.emptyMap() : SNAPSHOT.get();}/** 请求结束后清理,防止线程复用串数据 */public static void clear() {SNAPSHOT.remove();}
}
2.在 Web 入口(拦截器/过滤器)里拍照
@Component
public class HeaderSnapshotFilter extends OncePerRequestFilter {private static final Set<String> HEADER_NAMES = Set.of("Tenant-Id", "Authorization", "X-Request-Id", "X-User-Id", "X-User-Name");@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain)throws ServletException, IOException {try {RequestHeaderSnapshot.capture(HEADER_NAMES);chain.doFilter(request, response);} finally {RequestHeaderSnapshot.clear(); // 一定要清理}}
}
3.修改 FeignHeaderConfig,不再直接读 Request
@Bean
public RequestInterceptor headerRequestInterceptor() {return template -> {// 1. 读快照Map<String, String> snapshot = RequestHeaderSnapshot.get();snapshot.forEach(template::header);};
}
✅ 结果
-
Web 场景:主线程拍照 → Feign 在任何线程都能拿到快照。
-
非 Web 场景(定时任务、MQ):快照为空,Feign 只追加自定义参数,不会抛异常。
-
无侵入:不用改 Hystrix、Ribbon、异步线程池;快照用完后立即清理,线程安全。