You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3中如何在OkHttp请求拦截器中获取MDC上下文

解决OkHttp3拦截器无法访问MDC上下文的问题

问题核心在于:OkHttp的异步请求会在其内部线程池中执行,而MDC是线程绑定的,原请求线程的MDC上下文不会自动传递到OkHttp的工作线程中,导致拦截器里拿不到MDC数据。你已经通过Request.tag()将MDC上下文附加到请求上了,只需要在拦截器中取出并绑定到当前线程即可,同时要注意清理上下文避免线程池复用导致的污染。


1. 修改拦截器代码

在拦截器中取出请求tag里的MDC上下文,绑定到当前线程;执行完请求后恢复原线程的MDC状态,防止线程池复用带来的上下文污染:

@Slf4j
public class LoggerInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        // 从请求tag中获取预先存入的MDC上下文
        Map<String, String> requestMdcContext = chain.request().tag(Map.class);
        Map<String, String> originalThreadMdc = null;

        try {
            if (requestMdcContext != null) {
                // 保存当前线程原有的MDC(线程池复用场景下必须)
                originalThreadMdc = MDC.getCopyOfContextMap();
                // 将请求携带的MDC绑定到当前工作线程
                MDC.setContextMap(requestMdcContext);
            }

            log.info("MDC {}", MDC.getCopyOfContextMap()); // 现在可以获取到正确的MDC

            // 为请求添加关联ID请求头(假设关联ID的MDC key是correlationId)
            Request modifiedRequest = chain.request().newBuilder()
                    .header("X-Correlation-ID", MDC.get("correlationId"))
                    .build();

            return chain.proceed(modifiedRequest);
        } finally {
            // 恢复原线程的MDC上下文,或清空当前MDC
            if (originalThreadMdc != null) {
                MDC.setContextMap(originalThreadMdc);
            } else {
                MDC.clear();
            }
        }
    }
}

2. 优化请求调用逻辑(可选)

如果不想在每个请求里手动写tag(MDC.getCopyOfContextMap()),可以封装一个工具类自动处理MDC的传递:

@Component
public class OkHttpMdcCallFactory {
    private final OkHttpClient okHttpClient;

    public OkHttpMdcCallFactory(OkHttpClient okHttpClient) {
        this.okHttpClient = okHttpClient;
    }

    public Call newCall(Request.Builder requestBuilder) {
        // 自动将当前线程的MDC附加到请求tag中
        Map<String, String> currentMdc = MDC.getCopyOfContextMap();
        Request request = requestBuilder.tag(currentMdc).build();
        return okHttpClient.newCall(request);
    }
}

之后在业务代码里用这个工具类创建请求:

@Async
public CompletableFuture<Optional<JsonNode>> getEvent(String eventId) {
    log.info("MDC {}", MDC.getCopyOfContextMap());
    Request.Builder requestBuilder = new Request.Builder()
            .url(rsvpServicesProperties.getResource() + "/v2/events/" + eventId);
    
    CompletableFuture<Optional<JsonNode>> future = new CompletableFuture<>();
    okHttpMdcCallFactory.newCall(requestBuilder).enqueue(new Callback() {
        // 回调逻辑保持不变
        @Override
        public void onFailure(Call call, IOException e) {
            future.completeExceptionally(new IOException("Error occurred while getting " + call.request().url() + ": " + e.getMessage(), e));
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            try {
                if (response.isSuccessful()) {
                    future.complete(mapper.map(response.body().bytes()));
                } else {
                    future.complete(Optional.empty());
                }
            } catch (Exception e) {
                throw new IOException("Exception occurred while mapping response of " + call.request().url() + ": " + e.getMessage(), e);
            }
        }
    });
    return future;
}

内容的提问来源于stack exchange,提问作者fluminis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 12:16:11