当我们有阻塞调用时,我们应该使用像 spring webflux 这样的反应式堆栈 Web 框架吗?

2024-03-05

我试图了解什么时候我们会使用像 webflux 这样的反应式堆栈框架。 我读过的文章似乎表明,当我们有许多阻塞调用时,我们将从反应式方法中受益。例如,如果我们有一个 Webhook 服务,需要调用客户端服务器来更新信息。

但我也在这里读过https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html

评估应用程序的一个简单方法是检查其依赖性。如果您要使用阻塞持久性 API(JPA、JDBC)或网络 API,那么 Spring MVC 至少是通用架构的最佳选择。使用 Reactor 和 RxJava 在单独的线程上执行阻塞调用在技术上是可行的,但您不会充分利用非阻塞 Web 堆栈。

这似乎表明恰恰相反。 我读到,如果你可以流式传输信息,反应式会更有用。例如,如果您有一个前端要求一个列表和一个反应源,它可以在信息可用时为您提供信息,而不是在信息完成后仅提供全部信息。

那么问题是我们什么时候应该在后端使用响应式?当我们有很多阻塞调用时我们应该使用它吗?例如。对客户端的 HTTP 调用,我们需要等待响应。或者这到底是错误的用例?

我知道还有其他考虑因素,例如代码的复杂性。我知道反应式方法更难实现,但我在这里只是询问性能和可扩展性。


由于我们不知道您的确切架构,因此很难在这里给您任何具体答案。

但是,如果您了解反应式试图解决的问题以及它是如何解决的,那么您可能会更好地了解如何做出更好的决策。

大多数 Java Web 服务器使用的传统 servlet 方法为每个请求分配一个线程。因此,当收到请求时,就会为其分配一个线程,然后该线程处理该请求。如果您的服务器随后阻塞对其他服务器的调用,则分配的线程需要等待响应返回。

这往往会导致 Web 服务器有数百个线程,这些线程花费大量时间等待。当我说等待时,我的意思是等了很久。线程 90% 的时间都花在等待阻塞调用上。例如,Web 服务器中的处理可能需要 3 毫秒,然后它会执行阻塞数据库调用,并且线程需要等待 200 毫秒(不要引用我的数字)。

大量的资源都花在了等待上。

这么老的方法:

  • 每个请求一个线程
  • 如果有 300 个请求,那么我们就有 300 个线程
  • 内存占用高(每个线程都需要内存)
  • CPU花费大量时间等待

反应式通过使用事件循环和一个在事件循环上安排工作的小型线程池来解决这个问题。

在实践中,你可以这样看,一个事件循环,然后可能有 10 个线程,所有线程都调度工作,事件循环一直在工作,调度程序只是为事件循环安排一长串工作来推动。所以所有线程始终处于 100% 繁忙状态。

在 webflux 应用程序中,事件循环的数量通常取决于硬件中内核的数量。

但这意味着我们需要100%非阻塞。想象一下,在这种情况下我们有一个阻塞调用,然后整个事件循环将停止,所有计划的作业将停止,整个机器将冻结,直到我们“解除阻塞”。

如此反应:

  • 事件循环完成所有工作
  • 安排工作的小型线程池
  • 阻塞非常糟糕,可以冻结整个应用程序
  • 由于线程较少,内存占用较小
  • 更高的CPU使用率
  • 可能更高的吞吐量

所以我们基本上是用内存换取 CPU 能力。

那么什么是阻塞调用呢?大多数呼叫都会阻塞,因为您呼叫另一个服务并且需要等待。但这就是响应式的闪光点,因为它还有另一个功能。

由于每个请求没有一个特定的线程,因此任何线程都可以发出请求,但事情是这样的,任何线程都可以处理响应,它不必是同一个线程。

我们是所谓的线程不可知论者。

我们的非阻塞服务可以对其他服务进行大量阻塞调用,并且仍然保持完全非阻塞。因为当说非阻塞时,我的意思是我们在自己的应用程序内部是非阻塞的。

那么什么是错误的阻塞调用呢?好吧,当您调用与线程相关的东西时。这意味着您正在调用的东西依赖于发出调用来处理响应的同一线程,那么我们需要阻塞该线程并等待响应。

如果我们需要进行调用,然后阻塞等待响应,然后处理响应,因为我们需要相同的线程,那么我们就不能使用响应式,因为这样我们可能会阻塞事件循环,这将停止整个应用程序。

例如,所有使用的东西ThreadLocal在响应式世界中这很糟糕,这就是主要问题之一。 JDBC(数据库驱动程序规范)本质上是阻塞编写的。因为它依赖于本地线程来跟踪事务以便能够回滚。这意味着所有使用 JDBC 的数据库调用在非/阻塞反应式应用程序中都不可用,这意味着您必须使用使用 R2DBC 规范的数据库驱动程序。

但是 REST 调用不会阻塞,因为它不依赖于线程,除非您使用依赖于线程的功能,例如ThreadLocalSpring Webclient 没有。

那么你的引用在说什么? Spring Reactor 有一种机制,使您可以将“旧方式”(每个请求一个线程)与新方式(线程无关)混合在一起。这意味着,如果您有一个硬阻塞调用(使用 JDBC 驱动程序调用旧数据库),您可以明确告诉框架“对此数据库的所有调用都应该放在它自己的线程池上”,这样您就可以告诉框架使用旧方法(通过为该请求分配一个独占线程)进行该特定调用的框架。但请记住,这样做您将失去响应式的所有好处。

因此,如果您的服务仅调用大量硬阻塞服务(例如旧数据库),您将不得不不断选择退出反应式框架,因此您基本上只需使用反应式框架构建一个旧的传统 servlet Web 服务,即有点反模式。所以我不建议这样做。

我在这里写的只是一般计算机知识,线程如何工作,REST 调用如何工作,数据库驱动程序如何工作。我无法在单个堆栈溢出帖子中解释计算机如何工作。

这一点和更多内容都在反应堆参考 https://projectreactor.io/docs/core/release/reference/#intro-reactive我建议你做更多研究。

如果你的道路有很多转弯而没有直道,那么如果你经常需要减速并经常转弯,那么购买一辆 F1 赛车还有什么意义呢?

我把这个决定留给你。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当我们有阻塞调用时,我们应该使用像 spring webflux 这样的反应式堆栈 Web 框架吗? 的相关文章

随机推荐

  • 为什么我可以在模式匹配中将 :: 运算符与 Seq 一起使用,但在其他地方却不能

    所以我对 Scala 中 Seq 的这种行为感到非常困惑 使用模式匹配时 我可以使用 or 运算符 它们似乎可以互换 val s Seq 1 2 3 s match case x l gt 但是当我尝试使用时 在不同的情况下像这样 val
  • 如何使用 dnotify /inotify 命令持续监控目录

    我是 dnotify inotify 命令的新手 任何人都可以帮助我如何编写一个脚本 以便它持续监视目录并指示它有一些更改或修改 Inotify 本身是一个内核模块 可以通过调用来访问 例如一个 C 程序 https linux die n
  • 更新 AWS S3 对象密钥(密钥名称)

    我需要重命名 AWS S3 存储桶中存储的大量文件 每个文件 对象将单独重命名 基于特定标准 没有通用的 前缀 后缀 我想出了 current filename 和 new filename 的列表 它们可以作为 bat 文件 队列在 CL
  • 与 ANT 模式语法的混淆和可能的变化

    我正在开发 ANT 模式解析器 作为大型服务器项目的一部分 这篇文章的答案中有一些很好的 ANT 模式示例 如何使用 Nant Ant 命名模式 https stackoverflow com questions 69835 how do
  • Android 12 - 致命异常:android.content.res.Resources$NotFoundException

    我在 Android 12 设备上收到以下错误 Fatal Exception android content res Resources NotFoundException Resource ID 0x20c0025 at android
  • 将成员对象的引用添加到指针数组

    我有一个类 其中包含一些包含对象的私有成员和一个动态指针数组 我想用指向其中一些成员对象的指针填充该数组 class NextionTest public NextionDisplay private NexText lblText Nex
  • Sequelize 和 Postgres 按距点的距离排序

    我在搜索时遇到问题 包括按距某个点的距离排序 这是我的代码以及我正在尝试做的事情 感谢帮助 const Sequelize require sequelize var Flat db define flat id type Sequeliz
  • React 中的事件驱动方法?

    我想在一个组件中 触发一个事件 并让其他组件 订阅 该事件并在 React 中执行一些工作 例如 这是一个典型的 React 项目 我有一个模型 从服务器获取数据 并使用该数据呈现多个组件 interface Model id number
  • Android,在网络共享和连接到接入点时检测 WiFi 的本地 IP 和子网掩码

    我需要检测本地IP地址和子网掩码在 WiFi 网络上 在 Android 设备上 为了严格计算本地子网的 UDP 广播地址 当设备连接到接入点时 以下功能正常工作 Only works when NOT tethering WifiMana
  • Nuxt不会自动从嵌套目录导入组件

    在我的 nuxt 应用程序中 嵌套目录中的组件不会按预期自动导入 对于我的一些组件 我有如下内容 vue 2 6 12 nuxt 2 15 0 components 目录结构 TopArea SomeComponent vue
  • WPF 用户控件而非数据绑定

    在我的用户控件中 我有一个不是数据绑定的依赖属性 我查看了几篇堆栈溢出帖子 但我无法弄清楚我做错了什么 永远不会调用属性更改方法 到目前为止我的代码 我的用户控件基本上是一个增强的组合框 DP 位于用户控件内部 我正在数据网格内使用此用户控
  • 如何使用python将一幅图像的相位和不同图像的幅度组合成一幅图像

    我想将一幅图像的相位谱和不同图像的幅度谱组合成一幅图像 我得到了图像A和图像B的相位谱和幅度谱 这是代码 f np fft fft2 grayA fshift1 np fft fftshift f phase spectrumA np an
  • 数据库设计 - 一个表中的列引用两个表

    这是我所拥有的一个示例 以 Stack Overflow 为例 我有2张桌子 Questions and Answers 我也有一个Comments桌子 评论表将引用问题和答案 我应该如何设置数据库 评论中有 2 列 即 QuestionI
  • HTTP 响应中标头的顺序重要吗?

    标题的顺序是否有意义 A 1 B 2 vs B 2 A 1 我试图弄清楚是否可以使用字典来存储标题列表 或者它是否需要是某种列表或有序字典 不 具有不同名称的标头并不重要 看RFC 2616 https www rfc editor org
  • 使用 Q Promise 进行串行执行

    我想我误解了如何Q https github com kriskowal q承诺工作 我希望我的第一个承诺在下一个承诺开始之前得到解决 但这并没有发生 这是我的代码 var Q require q function doWork taskN
  • Apache Common CLI:如何添加参数?

    我正在将 Common CLI 用于个人项目 我从文档中没有找到的一件事是如何强制呈现某个参数 为了澄清我的问题 我可以定义参数和选项之间的区别 命令 mycommand file txt b 2 mycommand is the comm
  • Windows Phone 7 图像按钮

    我需要为我的应用程序创建一个图像按钮 例如面向网络的风格 我有一个 20x20 像素的图像 并且想要一个与图像尺寸相同的图像按钮 我尝试在我的 xaml 中设置它 但它不起作用
  • 使用 LLVM 将 x86 代码重新编译为更快的 x86

    是否可以输入 x86 32 位代码来运行 LLVM 编译器 有一个巨大的算法 我没有源代码 我想让它在相同的硬件上运行得更快 我可以通过优化将其从 x86 转换回 x86 吗 这段代码运行时间很长 所以我想对其进行静态重新编译 另外 我可以
  • Android——如何允许水平和垂直滚动

    ScrollView 只允许垂直滚动 Horizo ntalScrollView 只允许水平滚动 但两者都没有类 这似乎是 Android 用户界面中一个相当大的缺陷 有什么技巧可以实现这一点吗 Try this
  • 当我们有阻塞调用时,我们应该使用像 spring webflux 这样的反应式堆栈 Web 框架吗?

    我试图了解什么时候我们会使用像 webflux 这样的反应式堆栈框架 我读过的文章似乎表明 当我们有许多阻塞调用时 我们将从反应式方法中受益 例如 如果我们有一个 Webhook 服务 需要调用客户端服务器来更新信息 但我也在这里读过htt