14

阅读有关 Play Framework 和 ReactiveMongo 的文档让我相信 ReactiveMongo 的工作方式是使用很少的线程并且从不阻塞。

但是,从 Play 应用程序到 Mongo 服务器的通信似乎必须在某个线程上发生。这是如何实施的?Play、ReactiveMongo、Akka 等的源代码链接也将不胜感激。

Play 框架在此页面上包含一些关于线程池的文档。它开始:

Play 框架自下而上是一个异步 Web 框架。使用迭代器异步处理流。Play 中的线程池被调整为使用比传统 Web 框架更少的线程,因为 play-core 中的 IO 永远不会阻塞。

然后谈了一点 ReactiveMongo:

典型的 Play 应用程序最常阻塞的地方是它与数据库通信时。不幸的是,没有一个主要数据库为 JVM 提供异步数据库驱动程序,因此对于大多数数据库,您唯一的选择是使用阻塞 IO。一个值得注意的例外是 ReactiveMongo,它是 MongoDB 的驱动程序,它使用 Play 的 Iteratee 库与 MongoDB 对话。

以下是关于使用 Futures 的说明:

请注意,您可能会因此将阻塞代码包装在 Futures 中。这并不意味着它是非阻塞的,它只是意味着阻塞将发生在不同的线程中。您仍然需要确保您正在使用的线程池有足够的线程来处理阻塞。

在页面处理异步结果的播放文档中有类似的注释:

您不能通过将同步 IO 包装在 Future 中神奇地将其转换为异步。如果您无法更改应用程序的体系结构以避免阻塞操作,那么在某些时候必须执行该操作,并且该线程将阻塞。因此,除了将操作封装在 Future 中之外,还需要将其配置为在单独的执行上下文中运行,该执行上下文已配置了足够的线程来处理预期的并发性。

文档似乎在说 ReactiveMongo 是非阻塞的,因此您不必担心它会占用线程池中的大量线程。但是 ReactiveMongo 必须在某处与 Mongo 服务器通信。

这种通信是如何实现的,以便 Mongo 不会用完 Play 的默认线程池中的线程?

再一次,非常感谢PlayReactiveMongoAkka等中特定文件的链接。

4

1 回答 1

11

是的,确实,您仍然需要使用线程来执行任何类型的工作,包括与数据库的通信。重要的是这种沟通是如何发生的。

ReactiveMongo 在某种意义上“不使用线程”,它不使用阻塞 I/O。通常的 Java I/O 设施,例如java.io.InputStream阻塞;这意味着从这样的一个读取InputStream或写入OutputStream阻塞线程,直到“另一端”提供所需的数据或准备好接受它。对于网络通信,这意味着线程被阻塞。

但是,Java 提供了支持非阻塞异步I/O的NIO API 。我现在不想详细介绍它,但基本思想自然是非阻塞 I/O 允许不阻塞需要与外界交换一些数据的线程:例如,这些线程可以轮询数据源,检查是否有数据可用,如果没有,则返回线程池,可用于其他任务。当然,这些设施是由底层操作系统提供的。

非阻塞 I/O 的具体实现细节通常隐藏在像Netty这样的高级库中,因为它一点也不好用。例如,Netty(这正是 ReactiveMongo 使用的库)提供了很好的异步回调类 API,它非常易于使用,但也足够强大和富有表现力,可以构建具有高吞吐量的复杂 I/O 繁重的应用程序。

所以,ReactiveMongo 使用 Netty 与 Mongo 数据库服务器进行对话,而且由于 Netty 是异步网络 I/O 的实现,ReactiveMongo 确实不需要长时间阻塞线程。

于 2014-09-02T08:59:58.517 回答