【问题标题】:ThreadLocals on GraphQL-JavaGraphQL-Java 上的 ThreadLocals
【发布时间】:2020-11-25 01:50:51
【问题描述】:

我在 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 的异步特性无关(我希望也能利用这一点),而是与运行多个请求的潜在副作用有关p>

【问题讨论】:

    标签: java fork-join graphql-java


    【解决方案1】:

    据我所知,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

    【讨论】:

      猜你喜欢
      • 2012-10-24
      • 2020-01-05
      • 2019-05-11
      • 2011-04-21
      • 1970-01-01
      • 2010-12-27
      • 2019-03-22
      • 2020-01-30
      相关资源
      最近更新 更多