4

最近我在玩 Java 套接字和 NIO 来编写服务器。尽管我仍然不清楚为什么 Java NIO 会优于标准套接字。在使用这些技术中的任何一种编写服务器时,在大多数情况下,它归结为拥有一个接收连接并将它们进一步传递给工作线程的调度程序线程。

我已经读过,在线程模型中,每个连接都需要一个专用线程,但我们仍然可以创建一个固定大小的线程池并重用它们来处理不同的连接(从而降低创建和拆除线程的成本) .

但是对于 Java NIO,它看起来很相似。我们有一个接受请求的线程和一些在收到数据时处理数据的工作线程。

我发现 Java NIO 更好的一个例子是维护许多非繁忙连接的服务器,例如聊天客户端或 http 服务器。但无法真正理解为什么。

4

3 回答 3

5

有几个不同的原因。

  1. 使用带 a 的多路复用 I/OSelector可以为您节省大量线程,从而为您节省大量线程堆栈,从而为您节省大量内存。另一方面,它将调度从操作系统转移到您的程序中,因此它可能会花费您一些 CPU,并且还会花费您大量的编程复杂性。鉴于 select() 是在替代方案是更多进程而不是更多线程时设计的,事实上,额外的复杂性是否真的值得,而不是使用线程并将节省的编程资金花在更多内存上,这实际上是有争议的。

  2. MappedByteBuffers是一种比 java.io 或使用java.nio.channelswith略快的文件读取方式ByteBuffers

  3. 如果您只是从一个通道复制到另一个通道,使用“直接”缓冲区可以让您不必将数据从本机 JNI 空间复制到 JVM 空间并再次返回;或者使用 FileChannel.transferTo() 方法可以避免将数据从内核空间复制到用户空间。

于 2013-06-17T06:18:20.927 回答
1

尽管 NIO 支持 Dispatcher 模型,但 NIO 套接字默认情况下是阻塞的,当您这样使用它们时,对于小型 (< 100) 连接,它们可能比普通 IO 或非阻塞 NIO 更快。我还发现阻塞 NIO 比非阻塞 NIO 更易于使用。

当我想使用忙等待时,我使用非阻塞 NIO。这允许拥有一个永不放弃 CPU 的线程,但这仅在极少数情况下有用,即延迟很重要。

于 2013-06-15T22:41:06.123 回答
0

从我的基准测试来看,真正的优势(除了线程模型)是它消耗更少的内存带宽(内核<=>Java)。例如,如果您打开多个 UDP NIO 多播通道并具有高流量,您会注意到在一定数量的进程中,所有正在运行的 UDP 接收器的每个新进程的吞吐量都会降低。使用传统的套接字 API,我启动了 3 个具有全吞吐量的接收进程。如果我开始第四次,我会达到一个限制,并且在所有正在运行的进程中接收到的数据/秒将降低。使用 nio,我可以启动大约 6 个进程,直到出现这种效果。

我认为这主要是因为 NIO 直接桥接到本机或内核内存,而旧套接字将缓冲区复制到 VM 进程空间。

在 GRID 计算和高负载服务器应用程序(10GBit 网络或 infiniband)中很重要。

于 2013-06-15T22:26:26.340 回答