Tra*_*chs 5 java spring multithreading asynchronous
在Spring中,您可以让控制器返回Callable而不是T,它将立即释放请求处理线程并在WebAsyncManager管理的MvcAsync线程中计算结果.您只需将控制器方法内容包装在一个return () -> {... return result; };.很容易!
但重点是什么?
a)有500个请求处理线程并让它们完成所有工作有什么区别
b)只有几个请求处理线程并使用concurrencyLimit 500执行Callables中的所有请求?
第二个选项b)实际上对我来说看起来更糟,因为管理整个MvcAsync魔术需要花费很多.
我知道如何收获@Async方法一次执行两个方法并在完成后返回结果,但我显然不理解Callable控制器方法.
假设您有一个 Tomcat 服务器,它有 10 个线程侦听客户端请求。如果您有一个客户端调用需要 5 秒响应的端点,则该客户端将在这 5 秒内保留该线程。添加一些并发客户端,您很快就会在这 5 秒内耗尽线程。
情况更糟,因为在这 5 秒的大部分时间里,您的请求主要执行 I/O,这意味着您只是阻塞线程,只做等待。
所以,Spring 能够使用 Callable、CompletableFuture 或 ListenableFuture 作为控制器的返回类型,正是为了让程序员在一定程度上克服这类问题。
从根本上说,只返回这些类型中的一种只会释放 Web 服务器线程,使其可供其他客户端使用。因此,您可以在相同的时间内访问更多客户端,但是,这本身可能不足以实现非阻塞 IO(又名 NIO)API。
大多数这些特性来自 Servlet API 和 Servlet Async IO 提供的核心功能,Spring 可能应该在幕后使用它们。您可能想看看以下有趣的视频,它们帮助我从头开始理解这一点:
这些视频解释了 Servlet 异步 I/O 背后的想法以及实现 NIO Web 应用程序的最终目标。
这里的圣杯是达到一个点,在这个点上,线程池中的线程永远不会被阻塞,等待某些 I/O 发生。它们要么在做一些 CPU 绑定的工作,要么回到线程池中,在那里它们可以被其他客户端使用。当您执行 I/O 时,您不会引入等待,而是注册某种形式的回调,用于告诉您结果何时准备就绪,同时您可以使用宝贵的 CPU 内核处理其他事情。如果您仔细考虑一下,Callable、CompletableFuture 或 ListenableFuture 是 Spring 基础设施在幕后使用的那种回调对象,用于调用它们的功能来处理单独线程中的请求。
这增加了你的吞吐量,因为你可以同时处理更多的客户端,只需优化你宝贵的 CPU 资源的使用,特别是如果你以 NIO 方式来做,因为你可以想象,只是将请求移动到另一个线程,虽然有益(因为您释放了一个有价值的 Tomcat 线程),仍然会阻塞,因此,您只是将问题移至另一个线程池。
我相信这个基本原则也是 Spring 团队目前在Project Reactor 中所做的很大一部分工作的背后,因为为了利用此类功能的强大功能,您需要在 API 中引入异步编程,而这很难做。
这也是Netty、RxJava、Reactive Streams Initiative和Project Reactor等框架激增的原因。他们都在寻求推广这种类型的优化和编程模型。
还有一个有趣的新框架运动,它们利用了这个强大的特性,并试图与 Spring 竞争甚至补充该领域的有限功能。我说的是像Vert.x和Ratpack这样有趣的项目,现在我们正在研究它,这个特性也是Node.js的主要卖点之一。