每个客户一个线程。 可行?

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

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

(线程是从Executors.newCachedThreadPool()创建的,所以启动它们不会有任何重大开销)

我知道这是一个天真的设置,如果线程是专用的OS线程,它不会很好地扩展到许多连接。 但是,我听说Java中的多个线程可以共享一个硬件线程。 真的吗?

知道我将在Linux上使用Hotspot VM,在具有8核和12GB RAM的服务器上,你认为这个设置适用于成千上万的连接吗? 如果没有,有哪些替代方案?

这可能会扩展到数千个客户端。 但下一个问题是成千上万。

一种常见的替代方法是使用java.nio包中的Selectors和非阻塞I / O.

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

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

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

为了在处理许多套接字时具有良好的性能,通常使用一种select方法,即Unix API处理需要许多资源的单线程多套接字应用程序。

这可以通过java.nio包来完成,该包具有一个Selector类,它基本上可以通过所有打开的套接字并在新数据可用时通知您。

您在一个Selector注册所有已打开的流,然后您可以从一个线程处理所有这些流。

您可以在此处获得有关教程的其他信息

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

因此,创建数千个或更multithreading并不是一个好主意,因为它会影响您的性能( 上下文切换 , 缓存刷新/ 未命中 , 同步延迟等)。 如果你的CPU少于一千,那也没有任何意义。

并行服务许多客户端的唯一适当解决方案是使用异步I / O. 有关详细信息,请参阅Java NIO上的此答案 。

也可以看看:

  • 绿色线程
  • Solaris线程模型

试试Netty 。

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

线程并不像以前那么昂贵,因此“普通”的IO实现可以解决问题。 但是,如果您正在考虑扩展到数千或更高,那么可能值得研究一些更复杂的东西。

java.nio包通过提供套接字多路复用/非阻塞IO来解决这个问题,它允许您将多个连接绑定到一个Selector。 然而,由于multithreading和非阻塞方面,这种解决方案比简单的阻塞方法更难实现。

如果你想追求简单IO以外的东西,那么我建议你看一下那里的优质网络抽象库。 根据个人经验,我可以推荐Netty ,它可以为您完成大部分繁琐的NIO处理。 然而它确实有一点学习曲线但是一旦你习惯了基于事件的方法,它就会非常强大。

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

更新 :Matthew Schmidt的这篇文章声称Tomcat 6中基于NIO的连接器(由Filip Hanik编写)实现了16,000个并发连接。

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

我建议它更多地取决于服务器在处理消息时正在做什么。 如果它相对轻量级,那么你的机器规格应该易于处理仅仅处理成千上万个这样的过程的连接。 成千上万是另一个问题,但你只需要在同一个网络上的两台机器实际上经验性地测试它并获得明确的答案。

我认为更好的方法是不自己处理线程。 创建一个池(ThreadExecutor或其他一些东西)和简单的调度工作到您的池。

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

为什么要自己动手? 您可以使用带有servlet,消息队列或ZeroMQ的servlet容器。