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.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Resolving Context Loss in Asynchronous openFeign Calls Using RequestContextHolder

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<String, String> headers = getHeaders(httpServletRequest);
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            template.header(entry.getKey(), entry.getValue());
        }
    }

    /** Get original request headers */
    private Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> map = new LinkedHashMap<>();
        Enumeration<String> 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

OAuth2RequestContextHolderspring-cloudasync
Code Ape Tech Column
Written by

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

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.