java - Spring Webflux 或非阻塞模式不能不利于扩展
问题描述
我明白了线程是非阻塞的,我们不需要根据 N 个并发请求让线程蔓延,而是我们将我们的任务放在我们的响应式 Web 编程模式中的单个事件循环中。
是的,这会有所帮助,但是由于事件循环是一个队列,如果要处理的第一个任务永远阻塞怎么办?然后事件循环将永远不会进行,因此除了排队更多任务之外,响应和处理将结束。是的,超时可能是可能的,但我无法理解事件循环如何成为一个好的解决方案。
假设您有 3 个任务需要 3 秒来等待 IO 并运行每个执行并将它们提交到事件队列。然后它们仍然需要 9 秒才能被处理并在 IO 解决后执行。在使线程阻塞的情况下,这将在 3 秒内解决,因为它们同时运行。
我可以看到一个好处是,如果事件循环不是真正的队列,并且在发出任务已准备好处理的信号时,它会分派该任务以进行处理。但是,在这种情况下,这意味着任务执行的顺序不会被维护,并且每个任务仍然必须运行一个线程才能判断 IO 何时解决。
也许我没有正确理解事件循环和线程处理。有人可以纠正我吗,因为看起来这种反应堆模式似乎让事情变得更糟。
最后,在 Spring Reactor 中的 X 请求时,是否只创建 1 个线程来运行处理程序而不是传统的 X 线程?在那种情况下,如果有人不小心写了阻塞代码,那是不是意味着每个后续请求都会排队?
解决方案
将事件循环用于长时间运行的任务并不是一个好主意。这被认为是一种反模式。通常它仅用于快速获取即将发生的事件,但如果工作会明显阻塞事件循环,则不会实际执行与这些事件相关的工作。您可能希望使用单独的线程池来执行长时间运行的任务。因此,事件循环通常只会使用异步的非阻塞结构来启动工作(或者只有在可以非常快速完成的情况下才真正完成工作)并将较重且可能阻塞的任务传递给单独的线程池(用于 CPU 密集型计算) 或操作系统(例如要通过网络发送的数据缓冲区)。
此外,不要被只有一个线程在处理事件这一事实所迷惑,它非常快,通常对于要求苛刻的应用程序来说也足够了。NodeJS 之类的平台或 Netty 之类的框架(用于 Akka、Play 框架、Apache Cassandra 等)在其核心使用事件循环并取得了巨大成功。人们应该意识到一个事实,在事件循环中执行阻塞操作通常是一个坏主意。
请查看其中一些帖子以获取更多信息:
- 反应器模式和非阻塞 IO
- Unix网络编程
- Kotlin Webflux
- 稍微偏离主题但仍然是一个非常突出的例子:不要阻塞事件循环(NodeJS)