Java 非阻塞io与原始数据吞吐量上的阻塞io

Java 非阻塞io与原始数据吞吐量上的阻塞io,java,performance,io,nio,Java,Performance,Io,Nio,其中有一项声明: 与流行的观点相反,NIO在原始数据吞吐量方面的性能明显低于阻塞I/O。” 这是真的吗?有人能更详细地解释一下吗?典型的用例是什么 请求/响应处理需要解耦 当您可以处理请求、分派请求以便在其他执行上下文(不同的线程、对其他服务器的RPC调用、其他异步机制)上处理时,应使用非阻塞IO并释放web服务器的线程以处理更多传入请求。当响应处理完成时,将调用响应处理线程,并将响应发送到客户端 我建议阅读,以便更好地理解这个概念 至于更高的吞吐量:当您的服务器发送/接收大量数据时,所有这些上

其中有一项声明:

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

这是真的吗?有人能更详细地解释一下吗?典型的用例是什么

请求/响应处理需要解耦


当您可以处理请求、分派请求以便在其他执行上下文(不同的线程、对其他服务器的RPC调用、其他异步机制)上处理时,应使用非阻塞IO并释放web服务器的线程以处理更多传入请求。当响应处理完成时,将调用响应处理线程,并将响应发送到客户端

我建议阅读,以便更好地理解这个概念

至于更高的吞吐量:当您的服务器发送/接收大量数据时,所有这些上下文切换以及在线程之间传递数据都会影响整体性能。您需要做的只是将其保存到磁盘,然后返回OK。如果您刚刚将其扔到同一个线程中的磁盘上,开始在线程之间丢弃它可能会导致更多的mem copy操作。并且以异步方式处理此操作不会提高性能:尽管您可以释放请求句柄ng线程返回到web服务器的线程池,让它处理其他请求,您的主要性能瓶颈是磁盘IO,在这种情况下,尝试同时保存更多文件只会使速度变慢


我希望我说得足够清楚。如果需要更多解释,请在评论中提出更多问题。

只有当并发请求的数量相对较少(而不是成千上万)时,第一句话才是正确的。这都是关于使用多个线程(阻塞)而不是一个或几个线程(非阻塞)。假设您想编写一个只从远程服务器下载文件的应用程序。如果您的应用程序一次只需要下载一个文件,那么您只需要一个线程。但是,如果您有一个运行数千个HTTP请求的爬虫程序,那么您需要有数千个线程(或者使用有限数量的线程+NIO)。对于如此多的线程,问题在于上下文切换,这会显著降低应用程序的速度(因此对于如此多的并发请求,NIO更好)


但让我们回到您的问题。为什么NIO在原始数据吞吐量方面会较慢?原因是NIO驱动的应用程序使用的CPU时间量。在这种情况下,在阻塞模型中,您的代码只做一件事——等待数据(它在循环中执行recv()操作)。在NIO应用程序中,逻辑要复杂得多:在循环中,代码使用选择器选择一组密钥(涉及Linux、Oracle JVM上的epoll_wait系统调用),然后迭代该组密钥,为每个密钥选择一个通道,然后从通道中读取数据(操作系统中的read()操作).在标准的阻塞模型中,您所做的就是执行recv()系统函数。总之:在这种情况下,NIO驱动的应用程序会使用更多的CPU时间,并生成更多的模式切换操作,因为系统调用的数量更多(所谓模式切换,我指的是从用户模式切换到内核模式)。因此,下载文件所需的时间将更高。

因此,我是否可以得出结论,非阻塞io提供的最大优势是容量,即每台服务器可处理的并发请求量,而不是每请求处理的总吞吐量或速度?如果在群集中添加更多服务器,我是否可以实现如果我的应用程序体系结构具有良好的横向可扩展性,那么使用阻塞io可以获得更快的速度和吞吐量?大致可以。当然,您的所有服务器都使用一些共享资源(数据库、共享文件系统、一些后端服务)您可能会受到其速度的限制,但如果您没有类似的功能,在大多数情况下,添加更多服务器将使您获得更好的吞吐量。此外,异步编程更困难,更容易出错。另一方面,向群集添加更多服务器是一种扩展,可能无法帮助您处理大量的数据几乎不需要服务器时间的小请求(例如,使用非阻塞IO的web服务器对DOS攻击具有更大的弹性-它会向攻击者发送正确的错误代码,并执行有效请求所需的操作)明白了。谢谢你详尽的回答。但是如果几乎所有线程都被阻塞等待I/O,那么就没有上下文切换,我想……那么,对于许多并发请求使用线程,上下文切换成本真的是个问题吗?@Dobesvandermer操作系统调度程序无论如何都必须将资源花费在空闲线程上,以控制何时进行因此,是的,空闲线程也有成本。