2011 年的 Java:线程套接字 VS NIO:在 64 位操作系统和最新 Java 版本上选择啥?

Posted

技术标签:

【中文标题】2011 年的 Java:线程套接字 VS NIO:在 64 位操作系统和最新 Java 版本上选择啥?【英文标题】:Java in 2011: threaded sockets VS NIO: what to choose on 64bit OS and latest Java version?2011 年的 Java:线程套接字 VS NIO:在 64 位操作系统和最新 Java 版本上选择什么? 【发布时间】:2011-07-23 04:53:10 【问题描述】:

我在 *** 和一些博客上阅读了几篇关于 java.net 与 java.nio 的文章。但是我仍然不知道什么时候应该更喜欢 NIO 而不是线程套接字。请您检查一下我下面的结论,并告诉我哪些是不正确的,哪些是漏掉的?

由于在线程模型中,您需要为每个活动连接指定一个线程,并且每个线程为其堆栈占用大约 250 千字节的内存,对于每个套接字模型的线程,您将在大量并发连接时快速耗尽内存.不像蔚来。

在现代操作系统和处理器中,可以认为大量活动线程和上下文切换时间对性能几乎没有影响

NIO throughoutput 可能会更低,因为高负载环境中异步 NIO 库使用的 select() 和 poll() 比唤醒和进入睡眠线程更昂贵。

NIO 一直较慢,但它允许您处理更多并发连接。这本质上是时间/空间的权衡:传统 IO 速度更快但内存占用更大,NIO 速度较慢但使用的资源更少。

Java 对每个并发线程的硬限制为 15000 / 30000,具体取决于 JVM,这会将每个连接模型的线程限制为最大并发连接数,但 JVM7 没有这样的限制(无法确认此数据) .

所以,作为一个结论,你可以有这个:

如果您有数万个并发连接 - NIO 是更好的选择,除非请求处理速度是您的关键因素 如果您的线程数少于此值,则每个连接的线程是更好的选择(假设您有足够的 RAM 来将所有并发线程的堆栈保持在最大值) 对于 Java 7,无论哪种情况,您都可能希望超越 NIO 2.0。

我说的对吗?

【问题讨论】:

【参考方案1】:

这对我来说似乎是正确的,除了关于 Java 限制线程数量的部分——这通常受到它所运行的操作系统的限制(参见 How many threads can a Java VM support? 和 Can't get past 2542 Threads in Java on 4GB iMac OSX 10.6.3 Snow Leopard (32bit))。

要达到这么多线程,您可能需要调整 JVM 的堆栈大小。

【讨论】:

另一件需要注意的事情是,如果您使用所有这些连接来与服务器通信(即您是连接的客户端),您的连接数限制在大约 65,000 个,因为可用的本地端口数。 不,你不是。每个客户端都连接到同一个端口。顺便说一句,我拿那些线程,因为没有硬限制? Paul Tuma 在这里说的话:paultyma.blogspot.com/2008/03/… 它们连接到服务器端的同一个端口,但是客户端连接仍然需要在本地分配一个端口来接收来自服务器的响应。 @FractalizeR:是正确的。在当前的 TCP 实现中,每个传出连接都需要一个唯一的端口号。从理论上讲,TCP/IP 堆栈可以在分配传出端口时注意到元组;实际上,由于 API 的原因,这是不可能的:特别是 bind() 发生在 connect() 之前,无论是显式还是隐式。 @DobesVandermeer API 不是“为了它”,因为我给出的原因。【参考方案2】:

我仍然认为传统 IO 中线程的上下文切换开销很大。在较高的级别上,只有在多个线程不会争用相同的资源,或者它们花费的时间远高于资源上的上下文切换开销时,您才能获得性能。 提出这个问题的原因是,使用 SSD 等新存储技术,您的线程可以更快地回到 CPU 上进行竞争

【讨论】:

如果您的应用程序是网络 I/O 绑定的,但是,与 HTTP 客户端或服务器一样,那么所有“阻塞”线程将不会运行,直到内核唤醒它们,所以我不认为它们会导致任何上下文切换开销。这种切换开销仅适用于所有线程都试图同时运行以处理某些数据的应用程序。【参考方案3】:

构建 NIO 服务器没有单一的“最佳”方式,但 SO 上这个特定问题的优势表明人们认为有!您的问题总结了适合这两个选项的用例,足以帮助您做出适合您的决定。

此外,混合解决方案也是可能的!当线程打算做一些值得他们付出代价的事情时,你可以将通道交给线程,在更好的时候坚持使用 NIO。

【讨论】:

【参考方案4】:

我会说从每个连接的线程开始,如果遇到问题,可以从那里进行调整。

如果你真的需要处理一百万个连接,你应该考虑用 C(或其他)编写(或寻找)一个简单的请求代理,它每个连接使用的内存比任何 java 实现都少得多。代理可以异步接收请求并将它们排队到以您选择的语言编写的后端工作人员。

因此,每个活动请求的后端只需要一个线程,并且您可以只拥有固定数量的后端,因此内存和数据库的使用在某种程度上是预先确定的。当大量请求并行运行时,请求会等待更长的时间。

因此,我认为您永远不必在 64 位系统上使用 NIO 选择通道或异步 I/O (NIO 2)。每个连接的线程模型运行良好,您可以使用一些更合适的低级技术将连接扩展到“数万或数十万”。

避免过早的优化总是有帮助的(即在真正有大量连接进入之前编写 NIO 代码)并且尽可能不要重新发明***(Jetty、nginx 等)。

【讨论】:

【参考方案5】:

最常被忽视的是 NIO 允许零拷贝处理。例如。如果您在一台服务器上使用老式套接字从多个进程中侦听相同的多播流量,则任何多播数据包都会从网络/内核缓冲区复制到每个侦听应用程序。因此,如果您构建一个 GRID,例如20 个进程,你会遇到内存带宽问题。使用 nio,您可以检查传入缓冲区,而无需将其复制到应用程序空间。然后,该过程仅复制它感兴趣的部分传入流量。

另一个应用示例: 有关示例,请参见 http://www.ibm.com/developerworks/java/library/j-zerocopy/。

【讨论】:

以上是关于2011 年的 Java:线程套接字 VS NIO:在 64 位操作系统和最新 Java 版本上选择啥?的主要内容,如果未能解决你的问题,请参考以下文章

Java NIO套接字通道

java-----NIO总结

Java NIO 非阻塞模式 vs node.js 异步操作

Java NIO 非阻塞模式 vs node.js 异步操作

使用带有选择器的非阻塞模式下的 Java NIO 和 Unix 域套接字

Java网络编程中怎样用管道流