【问题标题】:How are thread pools "mapped" to future executions in Spring?线程池如何“映射”到 Spring 中的未来执行?
【发布时间】:2015-09-24 11:09:47
【问题描述】:

考虑一个代码:

@Autowired
private AsyncRestOperations restTemplate;

@RequestMapping("/abcd")
public CompletableFuture<Result> process(HttpRequest request) {
    convertListenableFutureToCompletableFuture(restTemplate.getForEntity("http://...", Result.class))
        .thenApply(/*Some logic here*/)
        .thenCompose(/*Some logic returns future*/)
}

这里可以看到如下处理顺序:

  1. Spring 接收到DispatcherServlet 的请求。
  2. DispatcherServlet 确定处理程序并将请求传递给它。
  3. process 方法被调用
  4. restTemplate.getForEntity 被调用。
  5. thenApply 被调用
  6. thenCompose 被调用
  7. CompletableFuture 被“返回”回DispatcherServlet(或其他 Spring 组件)

据我了解,1-37 点在同一个线程池中执行(是吗?)。

但是什么线程(池)用于执行点4-6

【问题讨论】:

  • 如果您发布了restTemplate bean的配置(我们只能看到接口定义),应该可以准确地告诉您调用第4步后会发生什么。
  • 只是Spring rest异步模板,像new AsyncRestTemplate()那样创建。

标签: java multithreading spring spring-mvc asynchronous


【解决方案1】:
  • 1-3 & 7 在同一个线程上执行(就像对 4 的调用一样)

  • 5,6 彼此在同一个线程上执行,这是提供 CompletableFuture 结果的同一个线程(在第 4 步中,很可能不是用于调用 restTemplate)。

  • AsyncRestOperations 是一个接口,所以内部发生的事情将取决于底层实现。

  • 如果您使用的是 NIO Rest 客户端,则 Rest 调用将在 NIO 客户端的内部事件循环池上执行(5 和 6 可能不是您想要的 - 请参阅 thenApplyAsync,然后是 thenCompose)。

  • 您正在使用异步阻塞 I/O Rest 客户端 & 通常调用将在其配置的线程池中的线程上执行 - 不幸的是,您使用的类的默认行为不是配置线程池但每次都使用一个新线程 - 见下文 - (这里在同一个线程上执行 5 和 6 可能很好,甚至是最佳的)。

更新

基于进一步的信息:该示例使用的是 AsyncRestTemplate,即

Spring 用于异步客户端 HTTP 访问的中心类。 ...

注意:默认情况下 AsyncRestTemplate 依赖于标准 JDK 工具 建立 HTTP 连接。您可以切换到使用不同的 HTTP 使用 Apache HttpComponents、Netty 和 OkHttp 等库 接受 AsyncClientHttpRequestFactory 的构造函数。

AsyncRestTemplate 使用 Spring 的 SimpleAsyncTaskExecutor(通过默认构造函数实例化时)

TaskExecutor 实现为每个任务启动一个新线程, 异步执行它。支持限制并发线程 通过“concurrencyLimit” bean 属性。默认情况下,数字 并发线程数是无限的。

注意:此实现不重用线程!考虑一个 线程池 TaskExecutor 实现,特别是对于 执行大量短期任务。

后面的部分可能不是最佳的,我会配置 AsyncRestTemplate 以使用您自己的线程池。

【讨论】:

  • 我在CompletableFuture 中读到了*Async 方法,但是Executor 我应该为它们使用什么? spring 是否有一个用于这些主题的,或者我应该创建一个带有线程池的新 bean,自动装配它并从中使用池?
  • 是的,我认为您必须使用 Spring 托管的其中一个,因为另一个构造函数采用 Spring 接口。例如新的 AsyncRestTemplate(新的 ThreadPoolTask​​Executor() ); - 创建一个大小不一的线程池。有 ThreadPoolExecutorFactoryBean 可用于以某种方式配置 JDK ExecutorServices。
  • @Cherry - 在这种情况下,您不需要 CompletableFuture 中的 *Async 方法。提交到任务执行器比在同一线程上执行慢约 10 倍(对于身份函数应用程序)。因为您没有使用 NIO,所以从 Async Rest 调用中恢复线程就可以了。
猜你喜欢
  • 1970-01-01
  • 2018-07-28
  • 2020-08-15
  • 2018-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多