非阻塞IO与阻塞IO在原始数据吞吐量方面的区别

8
apache HTTPComponent文档中有这样一句话:

与流行观点相反,就原始数据吞吐量而言,NIO的性能明显低于阻塞I/O。

这是真的吗?有人能详细解释一下吗?什么是典型的需要分离请求/响应处理的用例?
2个回答

3
当您可以处理请求并将其分派到某些其他执行上下文(不同的线程,对另一个服务器的RPC调用,某些其他异步机制)并释放Web服务器的线程以处理更多传入请求时,应使用非阻塞IO。当响应的处理完成后,将调用响应处理线程,并将其发送到客户端。
我建议阅读 netty documentation以更好地理解这个概念。
至于更高的吞吐量:当您的服务器发送/接收大量数据时,所有这些上下文切换和线程之间的数据传递可能会真正影响整体性能。像这样思考它:您收到了一个大请求(带有大文件的PUT请求)。您需要做的就是将其保存到磁盘并返回OK。开始在线程之间抛来抛去可能会导致需要进行更多的内存复制操作,而如果您只是在同一个线程中将其扔到磁盘上,则不需要进行这些操作。以异步方式处理此操作不会提高性能:尽管您可以将请求处理线程释放回Web服务器的线程池并让其处理其他请求,但您的主要性能瓶颈是磁盘IO,在这种情况下-尝试同时保存更多文件只会使事情变得更慢。

我希望我表达得足够清楚。如果您需要更多的解释,请在评论中随意提问。


那么我可以得出结论,非阻塞IO提供的最大优势是容量,即每个服务器可以处理的同时请求数量,而不是总吞吐量或每个请求处理的速度?如果我在集群中添加更多服务器,是否可以使用阻塞IO以更好的速度和吞吐量实现相同的结果,前提是我的应用程序架构具有良好的水平可扩展性? - xiefei
1
大致上是这样的。当然,如果您的服务器使用某些共享资源(数据库、共享文件系统、某些后端服务),则会受到其速度的限制,但如果您没有这样的资源,增加更多的服务器在大多数情况下将为您带来更好的吞吐量。此外,异步编程更难以处理,并且容易出现更多错误。 - RA.
1
另一方面,将更多的服务器添加到集群中是昂贵的,并且可能无法帮助您应对大量几乎不需要服务器时间的小请求(例如使用非阻塞IO的Web服务器更具有抗DOS攻击的能力-它会向攻击者响应适当的错误代码,并为有效请求执行所需的操作)。 - RA.
明白了。感谢您详尽的回答。 - xiefei

3
第一个陈述只有在并发请求的数量相对较小时才为真(通常是几十个而不是成千上万个)。这主要涉及使用许多线程(阻塞)而不是一个或几个线程(非阻塞)。假设你想写一个应用程序,它只从远程服务器下载一个文件。如果你的应用程序只需要一次下载一个文件,那么你只需要一个线程,但如果你有一个爬虫运行数千个HTTP请求,那么你就需要数千个线程(或使用有限数量的线程+NIO)。对于如此多的线程,问题在于上下文切换,这可能会严重减慢你的应用程序效率(因此对于这种并发请求数量,NIO更好)。
但是让我们回到你的问题上。为什么 NIO 在原始数据吞吐量方面可能会更慢?原因是 NIO 驱动的应用程序所使用的 CPU 时间。对于这种情况,在阻塞模式下,你的代码只做一件事——等待数据(在循环中执行 recv() 操作)。在 NIO 应用程序中,逻辑要复杂得多:在循环中,代码使用选择器选择一组键(这涉及 Linux、Oracle JVM 上的 epoll_wait 系统调用),然后遍历该集合,为每个键选择一个通道,然后从该通道读取数据(在操作系统中执行 read() 操作)。在标准阻塞模型中,你所做的一切就是执行 recv() 系统函数。总之:在这种情况下,使用 NIO 的应用程序会使用更多的 CPU 时间,并生成更多的模式切换操作,因为它涉及更高数量的系统调用(所谓的模式切换是指从用户模式切换到内核模式)。因此,下载文件所需的时间将更长。

但是,如果几乎所有的线程都被阻塞等待 I/O,那么我认为就没有上下文切换了...所以使用线程处理许多并发请求时,上下文切换成本真的是问题吗? - Dobes Vandermeer
@DobesVandermeer 操作系统调度程序必须花费资源来控制空闲线程是否需要被唤醒。因此,是的,空闲线程也有成本。 - vadipp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接