我知道这是一个反复出现的问题,我读过以下文章http://www.mailinator.com/tymaPaulMultithreaded.pdf这样的文章说nio比io更好地扩展。
但我很难看到java nio在开发Web服务器时如何比传统的acceptor / worker线程架构更好地扩展?让我解释一下:
通常,Java Web服务器使用以下模式来处理连接:
一些受限线程仅限于ServerSocket的accept()方法中的核心块数:
while (true) {
socket = serverSocket.accept();
// handleRequest submits the socket to a queue
handleRequest(socket);
socket.close();
}
当检索到客户端套接字时,它将被提交到非阻塞队列,然后由工作线程池中的工作线程处理。工作线程数取决于正在执行的操作的持续时间。
如何使用java.nio使这个架构更具可扩展性?
我的意思是我仍然需要工作线程来处理阻塞操作的请求(访问数据库或文件系统,调用外部服务)。如果后端操作没有像node.js那样异步执行,我仍然需要工作线程来限制整体可扩展性与1或2个事件调度程序线程的对比。
答案 0 :(得分:17)
我非常喜欢Paul Tyma关于这个问题的文章,它真的很深入。我在他的文章中看到两个要点:
使用非阻塞NIO的主要原因是当您有多个多个同时的空闲请求时。原因是:使用NIO,您可以从同一个线程提供多个请求,这是更好。
好的,这是你随处可读的内容。现在...... 为什么这个更好?
有两个主要原因,它们与每个线程带来的两种不同的开销有关:
因此,每个线程都会带来更多“浪费”的内存和可能“浪费”的处理器周期(以执行“上下文切换”)。
现在,假设您有一个聊天服务器,客户端建立HTTP连接请求新消息,并且只有当有新消息发送到该客户端时,您的服务器才会回答它们(以便客户端立即收到新消息)。假设你有10k这样的客户端。在传统的,阻塞的,每个连接的线程模型中,你有10k个线程。在Java中,线程堆栈大小(-Xss)的典型标准值是256kb。使用10k线程,您将自动使用大约2GB的内存!!!!!!!!更糟糕的是:即使聊天服务器上根本没有任何活动,也没有发送任何消息,客户端仍然会浪费那些2GB。添加大量的上下文切换,您就会发现问题。
在这种情况下,你最好使用非阻塞NIO,其中较少的线程(最终只有1!)足以处理所有10k客户端,因此你将保存上下文切换(即cpu)时间)和线程堆栈(即内存),即使以更复杂的代码为代价,这通常是使用非阻塞NIO的副作用。
答案 1 :(得分:3)
NIO或非阻塞IO可以很好地扩展以实现高并发性,您不需要为每个连接提供专用线程,您只需要一个主线程来接受连接并且需要一些其他工作线程来处理IO,线程数是固定的,这是它比传统的acceptor / worker线程架构更具可扩展性的主要原因。