接上一篇:https://blog.youkuaiyun.com/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 TransmittableThreadLocal<Map<String, String>> SNAPSHOT =
new TransmittableThreadLocal<>();
/** 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 (Func.isNotBlank(v)) {
map.put(h, v);
}
});
SNAPSHOT.set(map);
}
/** 任何地方获取快照 */
public static Map<String, String> get() {
return SNAPSHOT.get() == null ? Collections.emptyMap() : SNAPSHOT.get();
}
/* ---------- 外部写入(给 Hystrix 用)---------- */
public static void set(Map<String, String> snapshot) {
SNAPSHOT.set(snapshot == null ? Collections.emptyMap() : snapshot);
}
/** 请求结束后清理,防止线程复用串数据 */
public static void clear() {
SNAPSHOT.remove();
}
}
2.在 Web 入口(拦截器/过滤器)里拍照
@Component
@Order(Ordered.LOWEST_PRECEDENCE) // 最后执行
public class HeaderSnapshotFilter extends OncePerRequestFilter {
private static final Set<String> HEADER_NAMES =new HashSet<>(Arrays.asList(
"Authorization",
"X-Request-Id",
"X-User-Id",
"X-User-Name"
));
@Override
protected 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
@Configuration
public class FeignHeaderConfig {
@Bean
public RequestInterceptor headerRequestInterceptor() {
return template -> {
// 1. 读快照
Map<String, String> snapshot = RequestHeaderSnapshot.get();
snapshot.forEach(template::header);
};
}
}
4.修改HystrixConfig,获取快照中的内容,而不是请求头中的
@Configuration
public class HystrixConfig {
@Bean
public HystrixConcurrencyStrategy hystrixConcurrencyStrategy() {
return new HystrixConcurrencyStrategy() {
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
// 拿快照,而不是 RequestAttributes
Map<String, String> snapshot = RequestHeaderSnapshot.get();
return () -> {
try {
RequestHeaderSnapshot.set(snapshot); // 先写快照
return callable.call();
} finally {
RequestHeaderSnapshot.clear();
}
};
}
};
}
}
✅ 结果
-
Web 场景:主线程拍照 → Feign 在任何线程都能拿到快照。
-
非 Web 场景(定时任务、MQ):快照为空,Feign 只追加自定义参数,不会抛异常。
-
无侵入:不用改 Hystrix、Ribbon、异步线程池;快照用完后立即清理,线程安全。
解决Feign异步请求No thread-bound错误
1948

被折叠的 条评论
为什么被折叠?



