Java 每个客户端一个线程。可行吗?

Java 每个客户端一个线程。可行吗?,java,multithreading,networking,io,nio,Java,Multithreading,Networking,Io,Nio,我正在编写一个Java服务器,它使用普通套接字来接受来自客户端的连接。我使用的是一个相当简单的模型,其中每个连接都有自己的线程在阻塞模式下读取。伪代码: handshake(); while(!closed) { length = readHeader(); // this usually blocks a few seconds readMessage(length); } cleanup(); (线程是从执行器.newCachedThreadPool()创建的,因此启动线程时不

我正在编写一个Java服务器,它使用普通套接字来接受来自客户端的连接。我使用的是一个相当简单的模型,其中每个连接都有自己的线程在阻塞模式下读取。伪代码:

handshake();

while(!closed) {
  length = readHeader(); // this usually blocks a few seconds
  readMessage(length);
}

cleanup();
(线程是从执行器.newCachedThreadPool()创建的,因此启动线程时不会有任何明显的开销)

我知道这是一个有点幼稚的设置,如果线程是专用的操作系统线程,它就不能很好地扩展到许多连接。然而,我听说Java中的多个线程可以共享一个硬件线程。这是真的吗


知道我将在Linux上使用Hotspot虚拟机,在一个有8个内核和12GB内存的服务器上,你认为这个设置对数千个连接都适用吗?如果没有,还有什么选择

这可能会扩展到数千个客户端。但是,下一个问题是有多少人

一种常见的替代方法是使用选择器和包中的非阻塞I/O


最终,您会遇到这样一个问题:在集群配置中设置服务器,平衡多台物理机器上的负载是否有用。

这将很好地扩展到数百个连接,而不是数千个。一个问题是Java线程也占用了相当多的堆栈(例如256K),操作系统在调度所有线程时会遇到问题


查看Java NIO或framworks,它们将帮助您更轻松地开始做复杂的事情(例如Apache Mina)

为了在处理多个套接字时获得良好的性能,您通常使用
select
方法,这就是Unix API处理需要大量资源的单线程多套接字应用程序的方法

这可以通过
java.nio
包来完成,该包有一个
选择器
类,该类基本上能够遍历所有打开的套接字,并在新数据可用时通知您

您可以在一个
选择器中注册所有打开的流,然后可以从一个线程处理所有流


您可以通过教程获得更多信息线程不像以前那样昂贵,因此“普通”IO实现在某种程度上是可以接受的。然而,如果您正在考虑扩展到数千个或更多,那么可能值得研究更复杂的东西

nio包通过提供套接字多路复用/非阻塞IO解决了这个问题,它允许您将多个连接绑定到一个选择器。然而,由于多线程和非阻塞方面的原因,这种解决方案比简单的阻塞方法更难获得正确的结果


如果您希望追求简单IO之外的东西,那么我建议您查看一个高质量的网络抽象库。根据我个人的经验,我可以推荐哪一个为你做了最精细的NIO处理。不过,它确实有一点学习曲线,但一旦您习惯了基于事件的方法,它就会非常强大。

如果您对利用现有容器的部署和管理感兴趣,您可以考虑在Tomcat中创建一个新的协议处理程序。注意一个相关的问题

更新:Matthew Schmidt声称Tomcat 6中基于NIO的连接器(由Filip Hanik编写)实现了16000个并发连接

如果您想编写自己的连接器,请查看以帮助进行NIO抽象。MINA还具有管理功能,可以消除对另一个容器的需求(如果您关心许多单元的部署及其操作等)。

for正在使用一对一线程映射。这意味着每个Java线程都映射到一个本机OS线程

因此,创建一千个或更多线程不是一个好主意,因为它会影响性能(、刷新/延迟等)。如果您的CPU少于1000个,这也没有任何意义

对于并行服务多个客户机,唯一合适的解决方案是使用异步I/O。有关详细信息,请参阅

另见:

试试看


“每个请求一个线程”模型是大多数Java应用服务器的编写方式。您的实现可以像它们一样扩展。

我认为更好的方法是不自己处理线程。创建一个池(ThreadExecutor或其他一些东西)并将工作简单地分派到池中

当然,我认为异步I/O将使它更好更快,但会帮助您解决套接字和网络问题。只有当您的线程由于I/O而阻塞时,JVM会将其置于睡眠状态,并更改为另一个线程,直到阻塞I/O返回。但这只会阻塞线程。处理器将继续运行并开始处理其他线程。因此,减去创建线程的时间,使用I/O的方式对模型的影响不大。
如果您不创建线程(使用池),您的问题就解决了。

我建议这更确切地取决于服务器在处理消息时执行的其他操作。如果它相对轻量级,那么您的机器规范应该能够轻松处理数千个这样的进程的连接。数万台可能是另一个问题,但你只需要在同一个网络上使用两台机器就可以对其进行经验测试并得到明确的答案。

为什么要自己动手?您可以将servlet容器与servlet、消息队列或ZeroMQ一起使用。

每个线程都会使用一些堆栈,但如果我将其转换为非阻塞模型,则每个连接都需要堆上的更多数据,例如“我们现在处于读取消息的哪个阶段”,这是由指令指针(正确的字?)确定的一根线的长度。不过调度可能是个问题。@Bart:每个连接的额外空间远没有堆栈那么大;使用NIO将提高可伸缩性