GraphQL根解析器与字段解析器线程池差异及MDC传递问题
GraphQL根解析器与字段解析器线程池不一致导致MDC丢失的问题解决
问题1:为什么根解析器用自定义线程池,字段解析器却用OkHttp线程池?
这是因为两者的异步执行逻辑分属不同管控范围:
- 根解析器的异步执行由Spring的
AsyncTaskExecutor(也就是你定义的graphqlAsyncTaskExecutor)调度,受Spring异步框架管控,所以会使用你配置的自定义线程池。 - 字段解析器如果通过OkHttp发起异步HTTP请求(比如调用外部服务),OkHttp会使用自身内置的线程池处理请求回调,这部分脱离了Spring异步框架的管控,自然不会复用你的自定义线程池。另外,如果字段解析器返回
CompletableFuture但未指定自定义Executor,默认会使用JDK的ForkJoinPool.commonPool(),而非Spring线程池。
问题2:如何将MDC上下文复制到这些线程池?
如果这是预期行为,需要从两个层面处理MDC上下文传递:
1. 修复自定义线程池的MDC传递(根解析器部分)
你当前的自定义线程池未配置TaskDecorator,导致定义好的MdcContextTaskDecorator未生效,需给线程池添加装饰器:
@Bean(name = "graphqlAsyncTaskExecutor") public Executor newExecutor() { var executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(Runtime.getRuntime().availableProcessors()); executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors()); executor.setKeepAliveSeconds(0); executor.setThreadNamePrefix("hub-orch"); // 绑定MDC装饰器 executor.setTaskDecorator(new MdcContextTaskDecorator()); executor.initialize(); return new DelegatingSecurityContextAsyncTaskExecutor(executor); }
2. 处理OkHttp线程池的MDC传递
要让OkHttp线程池也能传递MDC,需给OkHttp配置自定义线程池,并给任务添加MDC装饰:
// 创建带MDC上下文传递的OkHttp客户端 private OkHttpClient createOkHttpClientWithMdc() { // 自定义OkHttp线程池 ThreadPoolExecutor okHttpThreadPool = new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(), 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> new Thread(r, "okhttp-hub-orch-" + new AtomicInteger().incrementAndGet()) ); // 给线程池的任务添加MDC装饰 Executor mdcDecoratedExecutor = command -> okHttpThreadPool.execute(new MdcContextTaskDecorator().decorate(command)); return new OkHttpClient.Builder() .dispatcher(new Dispatcher(mdcDecoratedExecutor)) .build(); }
3. 字段解析器内手动传递MDC(备选方案)
如果不想修改OkHttp全局配置,可在字段解析器的异步逻辑中手动捕获并传递MDC上下文:
@Override public CompletableFuture<Data> fetchData(String id) { // 捕获当前线程的MDC上下文 Map<String, String> mdcContext = MDC.getCopyOfContextMap(); return okHttpClient.newCall(request) .executeAsync() .thenApply(response -> { try { MDC.setContextMap(mdcContext); // 处理响应并返回数据 return parseResponse(response); } finally { MDC.clear(); } }); }
内容的提问来源于stack exchange,提问作者TARA MISHRA
相关产品推荐
相关产品推荐

