可调用/可运行控制器方法:重点是什么?

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控制器方法.

Edw*_*rzo 5

假设您有一个 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 中引入异步编程,而这很难做。

这也是NettyRxJavaReactive Streams InitiativeProject Reactor等框架激增的原因。他们都在寻求推广这种类型的优化和编程模型。

还有一个有趣的新框架运动,它们利用了这个强大的特性,并试图与 Spring 竞争甚至补充该领域的有限功能。我说的是像Vert.xRatpack这样有趣的项目,现在我们正在研究它,这个特性也是Node.js的主要卖点之一。

  • @KernelMode 将其与您的硬件架构的工作方式进行比较。您的处理器比硬盘驱动器快得多,当您想读取文件时,操作系统会注册一个中断,以便在磁盘缓冲区中有可用数据时通知您,同时,它会执行其他操作。CPU 总是很忙。当磁盘缓冲区有可用数据时,会产生一个中断,然后操作系统可以为其分配一些 CPU。所以,从来没有一个线程在等待。这与 NIO 的原理相同。代码从不阻塞线程。当有工作要做时,将分配一个线程来完成它。 (2认同)