繁体   English   中英

GraphQL-Java 上的 ThreadLocals

[英]ThreadLocals on GraphQL-Java

我在 GraphQL 上公开了一个旧的 Web 应用程序,但是这个 Web 应用程序使用了 Threadlocals(以及其他 Apache-Shiro)。

由于 GraphQL-java 似乎使用fork-join pool进行并发,我担心需要走多远才能确保我的 ThreadLocals 仍然可以正常工作并安全地工作。

阅读文档和来源,似乎并发的很大一部分是由返回CompletableFuture的 DataFetchers 实现的,我无法确定这是否是唯一的并发来源(我认为不是)以及DataFetchers本身是否是从fork-join pool

那么将我的DataFetcher包装在一个设置和清除 ThreadLocals 的委托中是否安全? 或者这是否仍然存在被抢占并继续在fork-join pool另一个线程上的风险,例如:

static class WrappedDataFetcher implements DataFetcher<Object> {
        private DataFetcher<?> realDataFetcher;

        WrappedDataFetcher(DataFetcher<?> realDataFetcher) {
            this.realDataFetcher = realDataFetcher;
        }

        @Override
        public Object get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception {
            try {
                setThreadLocalsFromRequestOrContext(dataFetchingEnvironment);
                return realDataFetcher.get(dataFetchingEnvironment);
            } finally {
                clearTreadLocals();
            }
        }
    }

或者我是否需要在线程池中显式运行我的 DataFetchers,例如:

    static class WrappedDataFetcherThreadPool implements DataFetcher<Object> {
        private DataFetcher<?> wrappedDataFetcher;
        private ThreadPoolExecutor executor;


        WrappedDataFetcherThreadPool(DataFetcher<?> realDataFetcher, ThreadPoolExecutor executor) {
            // Wrap in Wrapper from previous example to ensure threadlocals in the executor
            this.wrappedDataFetcher = new WrappedDataFetcher(realDataFetcher);
            this.executor = executor;
        }

        @Override
        public Object get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception {
            Future<?> future = executor.submit(() -> wrappedDataFetcher.get(dataFetchingEnvironment));
            return future.get(); //for simplicity / clarity of the question
        }
    }

我认为第二个解决了我的问题,但感觉有点矫枉过正,我担心性能。 但我认为第一个有抢占的风险。

如果有更好的方法来处理这个问题,我也很乐意听到。

注意:这不是关于 GraphQL 的异步性质(我也希望利用它),而是关于使用带有treadLocals 的多个请求可能会由于fork-join pool而在请求之间混淆的可能副作用

据我所知,graphql-java 不使用自己的线程池,而是依赖于应用程序。 它使用未来回调实现它的方式。 假设这是应用程序的当前状态。

具有线程本地存储 TLS_1 的线程 T_1 执行数据提取器 DF_1。

Graphql-java 引擎将同步回调附加到 DF_1 返回的未来。 如果未返回未来,它将结果包装在已完成的未来中,然后附加同步回调。 由于回调是同步的,完成未来的线程运行回调。 如果除 T_1 之外的任何其他线程完成未来,则 TLS_1 将丢失(除非它被复制到正在执行的线程)。 一个例子是一个非阻塞的 HTTP I/O 库,它使用一个 I/O 线程来完成未来的响应。

这是作者对 graphql-java 库中的线程行为进行了更多评论的链接

https://spectrum.chat/graphql-java/general/how-to-supply-custom-executor-service-for-data-fetchers-to-run-on~29caa730-9114-4883-ab4a-e9700f225f93

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM