我正在尝试编写一个HTTP API服务器,它对特定资源执行基本的CRUD操作。它与外部数据库服务器进行通信以执行操作。 scala中的未来支持非常好,并且对于所有非阻塞计算,将来使用未来。我已经在很多地方使用了未来,我们用未来包装操作并继续前进,当值最终可用并且回调被触发时。
来到HTTP API服务器的上下文,可以实现非阻塞异步调用,但是当GET
或POST
调用仍然阻塞主线程时?
我无法理解如何有效地设计HTTP服务器以最大限度地提高效率,当几百个请求到达特定端点时会发生什么以及如何处理它。我被告知光滑采取最好的方法。
如果有人能够解释未来成功的http请求生命周期,并且没有未来,假设有100个db连接线程。
答案 0 :(得分:2)
当发出GET请求时,成功200表示数据被写入 数据库成功而不丢失。直到数据写入 服务器,创建的线程仍然阻塞,直到最后 已从插入的数据库收到确认 成功吧?
根本不需要阻止为特定请求创建的线程。当你启动HTTP服务器时,你总是有“主”线程正在进行并等待请求进入。一旦请求开始,它通常被卸载到一个从线程池中取出的线程(或ExecutionContext
)。提供请求的线程不需要阻塞任何东西,它只需要注册一个回调,上面写着“一旦这个未来完成,请用成功或失败指示完成此请求”。同时,客户端套接字仍在等待服务器的响应,没有任何返回。例如,如果我们在Linux上并使用epoll
,那么我们会向内核传递一个文件描述符列表,以监视传入的数据并等待该数据变为可用,我们将在其中收到通知对
由于Linux实现了java.NIO
的原因,我们在JVM上运行时可以免费获得此功能。
主线程(收到http请求时创建)可以委派 并获得一个Future,但它仍然被阻止,直到onSuccess 触发当值可用时触发,这意味着 db调用成功。
主线程通常不会被阻止,因为它负责接受新的传入连接。如果你从逻辑上考虑它,如果主线程被阻塞直到你的请求完成,这意味着我们只能提供一个并发请求,谁想要一台一次只能处理一个请求的服务器?
为了能够接受多个请求,它永远不会处理它接受连接的线程上的路由处理,它总是会将它委托给后台线程来完成这项工作。
通常,在Linux和Windows中有很多方法可以实现高效的IO。前者有epoll,后者有IO completion ports。有关epoll
内部工作方式的详情,请参阅https://eklitzke.org/blocking-io-nonblocking-io-and-epoll
答案 1 :(得分:1)
首先,必须阻止最终主线程才能继续运行。但它与线程池和join
没有什么不同。我不确定你在这里问什么,因为我认为我们都同意使用线程/并发比单线程操作更好。
Future
简单而有效,因为它抽象了您的所有线程处理。默认情况下,所有新的期货都在全局隐式ExecutionContext中运行,这只是一个默认的线程池。一旦你发出Future
请求,该线程就会产生并运行,你的程序将继续执行。还有方便的构造来直接操纵未来的结果。例如,您可以在期货上map
和flatMap
,一旦未来(线程)返回,它将运行您的转换。
它不像单线程语言,如果你有一个阻塞调用,单个未来将实际阻止整个执行。
当你比较效率时,你将它与什么进行比较?
答案 2 :(得分:1)
一般情况下"非阻塞"可能在不同的上下文中表示不同的东西:非阻塞=异步(您的第二个问题)和非阻塞=非阻塞IO(您的第一个问题)。第二个问题有点简单(解决更传统或众所周知的问题),所以让我们从它开始。
主线程(在收到http请求时创建)可以委托并获取Future,但它仍然被阻塞,直到触发onSuccess,当值可用时触发,这意味着db调用成功。
它没有被阻止,因为Future在不同的线程上运行,所以执行db调用逻辑的主线程和线程并发运行(主线程仍然能够处理其他请求,而前一个请求的db调用代码正在执行)。
发出GET请求时,成功200意味着数据成功写入数据库而不会丢失。在将数据写入服务器之前,创建的线程仍然阻塞,直到从数据库收到插入成功的最终确认为止?
这方面是关于IO的。不必阻止线程制作DB调用(网络IO)。每个请求的旧"线程就是这种情况。模型,当线程确实被阻止,你需要为另一个DB请求创建另一个线程。然而,现在非阻塞IO变得流行。您可以谷歌获取有关它的更多详细信息,但通常它允许您使用一个线程进行多个IO操作。