【问题标题】:Why Tomcat is using different threads when request url is same and handled with CompletableFuture?为什么当请求 url 相同并使用 CompletableFuture 处理时,Tomcat 使用不同的线程?
【发布时间】:2020-11-06 15:05:41
【问题描述】:

我使用 CompletableFuture 在 github 中使用了 this sample

我尝试同时从浏览器(chrome)中的多个选项卡运行 /asyncCompletable 端点。 关键是 Tomcat 是 Servlet 容器,它有自己的 Servlet 线程池,因此从浏览器的不同选项卡中访问相同的端点 url 不一定使用相同的主线程。请参阅下面的控制台日志:

8:07:07.334 [http-nio-9191-exec-8] Request received
18:07:07.335 [http-nio-9191-exec-8] Servlet thread released
18:07:07.335 [ForkJoinPool.commonPool-worker-3] Start processing request
18:07:08.262 [http-nio-9191-exec-7] Request received
18:07:08.262 [http-nio-9191-exec-7] Servlet thread released
18:07:08.262 [ForkJoinPool.commonPool-worker-4] Start processing request
18:07:08.860 [http-nio-9191-exec-9] Request received
18:07:08.861 [http-nio-9191-exec-9] Servlet thread released
18:07:08.861 [ForkJoinPool.commonPool-worker-5] Start processing request
18:07:09.376 [http-nio-9191-exec-10] Request received
18:07:09.377 [http-nio-9191-exec-10] Servlet thread released

正如您所见,它为同一 URL 的每个浏览器选项卡使用不同的线程,而不是重用已发布的线程以进行进一步的调用。 我的意思是它不会为下一个传入请求重用已发布的线程(例如 http-nio-9191-exec-7)。 这是为什么呢?

【问题讨论】:

  • 为什么应该
  • 因为 CompletableFuture 保证当前线程(主线程)不会阻塞,并且对于任何异步调用都会分配一个新线程。但是从我的日志中可以看出,主线程(具有 http-nio-9191-exec- 前缀)并不相同,尽管在客户端调用时立即释放。
  • 所以?您还没有解释为什么线程应该被立即重用,而不是(显然是这样)放入队列中以供重用。
  • 所以你的意思是,它又被放到了线程池中,可供其他传入的请求使用?
  • 这是我的猜测。你还没有解释为什么你期望你期望的特定行为。

标签: java spring-boot completable-future


【解决方案1】:

线程池就是它所说的 - 一个池。因此,池中不能保证将同一线程重新寻址到任务,也不会有任何帮助。池只返回池中的下一个可用线程。但“下一个”是什么意思?这取决于池的实现——这应该从用户那里抽象出来。仅仅因为它没有直接重新分配下一个请求并不意味着它永远不会被重用。池决定如何执行此操作。这取决于池的策略。因此,Servlet 池中可能存在一定的时间余量或阈值或一些队列。

当您重新配置 Tomcat 以进行评估时,如果行为发生变化,您可以尝试:

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="0"/>

但这对性能没有帮助,不建议这样做。除非有充分的理由,否则我不会弄乱 Servlet 线程池机制。池是出于充分的理由移交该控制权。

【讨论】:

  • 谢谢,我实际上在application.properties中配置了“server.tomcat.max-threads=1”并进行了测试。由于现在 Tomcat 在其线程池中有 1 个线程,因此对端点的多次调用(即“/asyncCompletable”)将仅使用 1 个线程,并且我的日志显示每次调用都有一个主线程,这在 CompletableFuture 的 commonPool 中使用了新线程用于运行异步任务。
  • 但是现在出现了另一个问题,因为我已经将 Tomcat 配置为在调用 /asyncCompltable 端点时只有一个线程,该端点使用的是 CompletableFuture.supplyAsync 方法,它仍然使用与主线程不同的线程吗?怎么样?
  • 引用:“问题是,supplyAsync() 默认使用 ForkJoinPool.commonPool(),线程池在所有 CompletableFutures、所有并行流和部署在同一个 JVM 上的所有应用程序之间共享(如果你不幸仍然使用具有许多已部署工件的应用程序服务器)。这个硬编码、不可配置的线程池完全超出了我们的控制范围,难以监控和扩展。因此,您应该始终指定自己的 Executor..." nurkiewicz.com/2015/11/which-thread-executes.html
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-19
  • 1970-01-01
  • 1970-01-01
  • 2013-12-27
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
相关资源
最近更新 更多