Resolving Context Loss in Asynchronous openFeign Calls Using RequestContextHolder
This article explains why asynchronous openFeign calls trigger Sentinel fallback due to missing request context, and provides a practical solution that copies the main thread's RequestAttributes into async threads to preserve JWT tokens and other headers.
When using openFeign for asynchronous calls, developers often encounter failures and Sentinel fallback because the request context (e.g., JWT token) is lost in the new thread, while synchronous calls work fine.
The root cause is that Spring stores request information in RequestContextHolder , which relies on ThreadLocal . Asynchronous threads do not inherit this data, leading to missing headers during remote calls.
To fix the issue, capture the current thread's RequestAttributes before launching the async task and set it inside each async thread before invoking the Feign client.
Example of the original asynchronous call:
CompletableFuture<T> future1 = CompletableFuture.supplyAsync(() -> {
// openfeign call
return feign.remoteCall();
}, executor);
CompletableFuture<T> future2 = CompletableFuture.supplyAsync(() -> {
// openfeign call
return feign.remoteCall();
}, executor);
CompletableFuture.allOf(future1, future2).join();Interceptor to propagate headers:
@Component
@Slf4j
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
HttpServletRequest httpServletRequest = RequestContextUtils.getRequest();
Map
headers = getHeaders(httpServletRequest);
for (Map.Entry
entry : headers.entrySet()) {
template.header(entry.getKey(), entry.getValue());
}
}
/** Get original request headers */
private Map
getHeaders(HttpServletRequest request) {
Map
map = new LinkedHashMap<>();
Enumeration
enumeration = request.getHeaderNames();
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
if (StrUtil.equals(OAuthConstant.TOKEN_NAME, key)) {
map.put(key, value);
break;
}
}
}
return map;
}
}Applying the context transfer in the async tasks:
// Capture main thread request attributes
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<T> future1 = CompletableFuture.supplyAsync(() -> {
// Set attributes for async thread
RequestContextHolder.setRequestAttributes(attributes);
// openfeign call
return feign.remoteCall();
}, executor);
CompletableFuture<T> future2 = CompletableFuture.supplyAsync(() -> {
RequestContextHolder.setRequestAttributes(attributes);
return feign.remoteCall();
}, executor);
CompletableFuture.allOf(future1, future2).join();By propagating the request attributes, the JWT token and other necessary headers are retained, preventing fallback and ensuring successful asynchronous Feign calls.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.