每个连接模型的 Java 线程与 NIO

非阻塞 Java NIO 是否仍然比每个连接异步套接字的标准线程慢?

此外,如果要对每个连接使用线程,是只创建新线程,还是使用非常大的线程池?

我正在用Java编写一个MMORPG服务器,如果硬件足够强大,它应该能够轻松扩展10000个客户端,尽管客户端的最大数量是24000(我相信由于Java中的15000线程限制,每个连接模型的线程都无法达到)。从一篇三年前的文章中,我听说每个连接模型的线程阻塞IO仍然比NIO快25%(即本文档 http://www.mailinator.com/tymaPaulMultithreaded.pdf),但是今天仍然可以实现相同的目标吗?从那时起,Java已经发生了很大的变化,我听说在比较现实生活场景时,结果值得怀疑,因为使用的VM不是Sun Java。此外,由于它是一个MMORPG服务器,有许多并发用户相互交互,因此使用同步和线程安全措施是否会降低性能,以至于为10000个客户端提供服务的单线程NIO选择器会更快?(所有工作都不需要使用选择器在线程上处理,它可以在工作线程上进行处理,就像MINA / Netty的工作方式一样)。

谢谢!


答案 1

蔚来的好处应该带着一粒盐来对待。

在HTTP服务器中,大多数连接都是保持活动状态的连接,它们大多数时候都是空闲的。为每个线程预先分配一个线程是浪费资源。

对于MMORPG来说,事情是非常不同的。我猜连接一直忙于接收用户的说明并向用户发送最新的系统状态。大多数情况下,连接需要线程。

如果使用 NIO,则必须不断为连接重新分配线程。它可能是一个劣质的解决方案,而不是简单的固定线程每个连接的解决方案。

默认线程堆栈大小非常大(1/4 MB?)这是为什么只能有有限线程的主要原因。尝试减少它,看看你的系统是否可以支持更多。

但是,如果您的游戏确实非常“繁忙”,那么您最需要担心的是您的CPU。无论是否NIO,在一台机器上处理数千名超级活跃的游戏玩家真的很难。


答案 2

实际上有3种解决方案:

  1. 多线程
  2. 一个线程和 NIO
  3. 同时使用解决方案 1 和 2

为了提高性能,最好的办法是让少量有限数量的线程,并在新消息通过网络传入时,使用NIO将网络事件多路复用到这些线程上。


在一个线程中使用NIO是一个坏主意,原因如下:

  • 如果您有多个 CPU 或内核,则资源将处于空闲状态,因为如果只有一个线程,则一次只能使用一个内核。
  • 如果您由于某种原因(可能是为了进行磁盘访问)而必须阻止,那么当您在等待磁盘时,CPU处于空闲状态。

每个连接一个线程是一个坏主意,因为它无法扩展。假设有:

  • 10 000 位好友
  • 2 个 CPU,每个 CPU 有 2 个内核
  • 在任何给定时间,只有 100 个线程将被阻塞

然后你可以计算出你只需要104个线程。再多了,你正在浪费资源来管理你不需要的额外线程。管理10 000个线程需要大量的簿记工作。这会减慢您的速度。


这就是您将这两种解决方案结合起来的原因。此外,请确保 VM 使用的是最快的系统调用。每个操作系统都有自己独特的系统,需要高性能网络IO。确保 VM 使用的是最新且最出色的 VM。我相信这是 Linux 中的 epoll()。

此外,如果要对每个连接使用线程,是只创建新线程,还是使用非常大的线程池?

这取决于您希望花费多少时间来优化。最快的解决方案是在需要时创建线程和字符串等资源。然后,让垃圾回收在完成它们时声明它们。您可以通过拥有资源池来提高性能。您不是创建一个新对象,而是向池请求一个对象,并在完成后将其返回到池中。这增加了并发控制的复杂性。这可以通过高级并发算法(如非阻塞算法)进一步优化。新版本的Java API为您提供了其中的一些。您可以在一个程序上度过余生进行这些优化。对于您的特定应用程序来说,最佳解决方案可能是一个值得自己发布的问题。


推荐