由于我们不知道您的确切架构,因此很难在这里给您任何具体答案。
但是,如果您了解反应式试图解决的问题以及它是如何解决的,那么您可能会更好地了解如何做出更好的决策。
大多数 Java Web 服务器使用的传统 servlet 方法为每个请求分配一个线程。因此,当收到请求时,就会为其分配一个线程,然后该线程处理该请求。如果您的服务器随后阻塞对其他服务器的调用,则分配的线程需要等待响应返回。
这往往会导致 Web 服务器有数百个线程,这些线程花费大量时间等待。当我说等待时,我的意思是等了很久。线程 90% 的时间都花在等待阻塞调用上。例如,Web 服务器中的处理可能需要 3 毫秒,然后它会执行阻塞数据库调用,并且线程需要等待 200 毫秒(不要引用我的数字)。
大量的资源都花在了等待上。
这么老的方法:
- 每个请求一个线程
- 如果有 300 个请求,那么我们就有 300 个线程
- 内存占用高(每个线程都需要内存)
- CPU花费大量时间等待
反应式通过使用事件循环和一个在事件循环上安排工作的小型线程池来解决这个问题。
在实践中,你可以这样看,一个事件循环,然后可能有 10 个线程,所有线程都调度工作,事件循环一直在工作,调度程序只是为事件循环安排一长串工作来推动。所以所有线程始终处于 100% 繁忙状态。
在 webflux 应用程序中,事件循环的数量通常取决于硬件中内核的数量。
但这意味着我们需要100%非阻塞。想象一下,在这种情况下我们有一个阻塞调用,然后整个事件循环将停止,所有计划的作业将停止,整个机器将冻结,直到我们“解除阻塞”。
如此反应:
- 事件循环完成所有工作
- 安排工作的小型线程池
- 阻塞非常糟糕,可以冻结整个应用程序
- 由于线程较少,内存占用较小
- 更高的CPU使用率
- 可能更高的吞吐量
所以我们基本上是用内存换取 CPU 能力。
那么什么是阻塞调用呢?大多数呼叫都会阻塞,因为您呼叫另一个服务并且需要等待。但这就是响应式的闪光点,因为它还有另一个功能。
由于每个请求没有一个特定的线程,因此任何线程都可以发出请求,但事情是这样的,任何线程都可以处理响应,它不必是同一个线程。
我们是所谓的线程不可知论者。
我们的非阻塞服务可以对其他服务进行大量阻塞调用,并且仍然保持完全非阻塞。因为当说非阻塞时,我的意思是我们在自己的应用程序内部是非阻塞的。
那么什么是错误的阻塞调用呢?好吧,当您调用与线程相关的东西时。这意味着您正在调用的东西依赖于发出调用来处理响应的同一线程,那么我们需要阻塞该线程并等待响应。
如果我们需要进行调用,然后阻塞等待响应,然后处理响应,因为我们需要相同的线程,那么我们就不能使用响应式,因为这样我们可能会阻塞事件循环,这将停止整个应用程序。
例如,所有使用的东西ThreadLocal
在响应式世界中这很糟糕,这就是主要问题之一。 JDBC(数据库驱动程序规范)本质上是阻塞编写的。因为它依赖于本地线程来跟踪事务以便能够回滚。这意味着所有使用 JDBC 的数据库调用在非/阻塞反应式应用程序中都不可用,这意味着您必须使用使用 R2DBC 规范的数据库驱动程序。
但是 REST 调用不会阻塞,因为它不依赖于线程,除非您使用依赖于线程的功能,例如ThreadLocal
Spring Webclient 没有。
那么你的引用在说什么? Spring Reactor 有一种机制,使您可以将“旧方式”(每个请求一个线程)与新方式(线程无关)混合在一起。这意味着,如果您有一个硬阻塞调用(使用 JDBC 驱动程序调用旧数据库),您可以明确告诉框架“对此数据库的所有调用都应该放在它自己的线程池上”,这样您就可以告诉框架使用旧方法(通过为该请求分配一个独占线程)进行该特定调用的框架。但请记住,这样做您将失去响应式的所有好处。
因此,如果您的服务仅调用大量硬阻塞服务(例如旧数据库),您将不得不不断选择退出反应式框架,因此您基本上只需使用反应式框架构建一个旧的传统 servlet Web 服务,即有点反模式。所以我不建议这样做。
我在这里写的只是一般计算机知识,线程如何工作,REST 调用如何工作,数据库驱动程序如何工作。我无法在单个堆栈溢出帖子中解释计算机如何工作。
这一点和更多内容都在反应堆参考 https://projectreactor.io/docs/core/release/reference/#intro-reactive我建议你做更多研究。
如果你的道路有很多转弯而没有直道,那么如果你经常需要减速并经常转弯,那么购买一辆 F1 赛车还有什么意义呢?
我把这个决定留给你。