3

我正在尝试编写一个对特定资源执行基本 CRUD 操作的 HTTP API 服务器。它与外部数据库服务器对话以执行操作。scala 中的未来支持非常好,并且对于所有非阻塞计算,都使用了未来。我在很多地方都使用过未来,我们用未来包装一个操作并继续前进,当值最终可用并触发回调时。

来到 HTTP API 服务器的上下文,可以实现非阻塞异步调用,但是当一个GET或一个POST调用仍然阻塞主线程时,对吗?

  1. 发出 GET 请求时,成功 200 表示数据已成功写入数据库且未丢失。在将数据写入服务器之前,创建的线程仍然处于阻塞状态,直到从数据库接收到插入成功的最终确认,对吧?
  2. 主线程(在收到 http 请求时创建)可以委托并返回 Future,但它是否仍然阻塞,直到触发 onSuccess,当值可用时触发,这意味着 db 调用成功。

我无法理解 HTTP 服务器的设计效率如何以最大限度地提高效率,当数百个请求到达特定端点时会发生什么以及如何处理它。有人告诉我,slick 采用了最好的方法。

假设有 100 个 db 连接线程,如果有人可以用未来和没有未来来解释成功的 http 请求生命周期。

4

3 回答 3

3

发出 GET 请求时,成功 200 表示数据已成功写入数据库且未丢失。在数据写入服务器之前,创建的线程仍然处于阻塞状态,直到从数据库接收到插入成功的最终确认,对吧?

为特定请求创建的线程根本不需要被阻塞。当你启动一个 HTTP 服务器时,你总是有一个“主”线程在运行并等待请求进来。一旦一个请求开始,它通常被卸载到一个从线程池(或ExecutionContext)中获取的线程。服务请求的线程不需要阻塞任何东西,它只需要注册一个回调,上面写着“一旦这个未来完成,请完成这个请求并指示成功或失败”。同时,客户端套接字仍在等待来自您的服务器的响应,没有任何返回。例如,如果我们在 Linux 上并使用epoll,那么我们向内核传递文件描述符列表以监视传入数据并等待该数据可用,

java.NIO由于在Linux 上的实现方式,我们在 JVM 上运行时免费获得此功能。

主线程(在收到 http 请求时创建)可以委托并返回 Future,但它是否仍然被阻塞,直到触发 onSuccess,当值可用时触发,这意味着 db 调用成功。

主线程通常不会被阻塞,因为它负责接受新的传入连接。如果你从逻辑上考虑,如果主线程阻塞直到你的请求完成,这意味着我们只能服务一个并发请求,谁想要一个一次只能处理一个请求的服务器?

为了能够接受多个请求,它永远不会在它接受连接的线程上处理路由的处理,它总是将它委托给后台线程来完成这项工作。

一般来说,在 Linux 和 Windows 中都有很多方法可以进行高效的 IO。前者有epoll而后者有IO 完成端口。有关epoll内部工作原理的更多信息,请参阅https://eklitzke.org/blocking-io-nonblocking-io-and-epoll

于 2017-11-19T10:00:05.190 回答
1

一般来说,“非阻塞”在不同的上下文中可能意味着不同的东西:非阻塞 = 异步(你的第二个问题)和非阻塞 = 非阻塞 IO(你的第一个问题)。第二个问题稍微简单一些(比如说解决更传统或众所周知的方面),所以让我们从它开始。

主线程(在收到 http 请求时创建)可以委托并返回 Future,但它是否仍然阻塞,直到触发 onSuccess,当值可用时触发,这意味着 db 调用成功。

它没有被阻塞,因为 Future 在不同的线程上运行,因此您的主线程和执行数据库调用逻辑的线程同时运行(主线程仍然能够在执行前一个请求的数据库调用代码时处理其他请求)。

发出 GET 请求时,成功 200 表示数据已成功写入数据库且未丢失。在将数据写入服务器之前,创建的线程仍然处于阻塞状态,直到从数据库接收到插入成功的最终确认,对吧?

这方面是关于IO的。不需要阻塞进行 DB 调用(网络 IO)的线程。旧的“每个请求线程”模型就是这种情况,当线程真的被阻塞并且您需要为另一个数据库请求创建另一个线程时。然而,现在非阻塞 IO 变得流行起来。您可以 google 以了解有关它的更多详细信息,但通常它允许您使用一个线程进行多个 IO 操作。

于 2017-11-19T11:15:43.587 回答
1

首先,必须有一些东西阻塞最终的主线程才能继续运行。但这与拥有一个线程池并join对其进行处理没有什么不同。我不确定你在这里问什么,因为我认为我们都同意使用线程/并发比单线程操作更好。

Future简单高效,因为它从您那里抽象出所有线程处理。默认情况下,所有新的期货都在全局隐式 ExecutionContext 中运行,这只是一个默认线程池。一旦你启动一个Future请求,那个线程就会产生并运行,你的程序将继续执行。还有一些方便的结构可以直接操纵未来的结果。例如,您可以map, 和flatMap在期货上,一旦该期货(线程)返回,它将运行您的转换。

它不像单线程语言,如果你有一个阻塞调用,一个未来实际上会阻塞整个执行。

当您比较效率时,您将其与什么进行比较?

于 2017-11-18T19:29:13.583 回答