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
相关产品推荐
相关产品推荐

