我正在编写一个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的服务器上,您认为此设置适用于数千个连接吗?如果没有,有哪些替代方案?
答案 0 :(得分:5)
这可能会扩展到数千个客户端。但是下一个问题是<多> 数千个。
常见的替代方法是使用java.nio
包中的选择器和非阻塞I / O.
最后,您会遇到在群集配置中设置服务器是否有用,平衡多台物理机上的负载的问题。
答案 1 :(得分:5)
这可以很好地扩展到数百个连接,而不是数千个连接。一个问题是Java线程也需要相当多的堆栈(例如256K),并且操作系统在安排所有线程时都会遇到问题。
查看Java NIO或framworks,它们将帮助您更轻松地开始复杂的工作(例如Apache Mina)
答案 2 :(得分:3)
为了在处理许多套接字时获得良好的性能,通常使用select
方法,即Unix API处理需要许多资源的单线程多套接字应用程序。
这可以通过java.nio
包来完成,该包具有Selector
类,基本上可以通过所有打开的套接字并在新数据可用时通知您。
您在一个Selector
内注册所有已打开的流,然后您可以从一个线程处理所有这些流。
您可以使用教程here
获取其他信息答案 3 :(得分:3)
JVM的Linux正在使用一对一线程映射。这意味着每个Java线程都映射到一个本机OS线程。
因此,创建数千个或更多线程并不是一个好主意,因为它会影响您的效果(context switching,cache刷新/ misses,synchronization延迟等) 。如果你的CPU少于一千个,那也没有任何意义。
并行服务多个客户端的唯一适当解决方案是使用异步I / O.有关详细信息,请参阅this answer上的Java NIO。
另见:
答案 4 :(得分:2)
尝试Netty。
“每个请求一个线程”模型是大多数Java应用服务器编写的方式。您的实施可以像他们一样扩展。
答案 5 :(得分:1)
线程并不像以前那么昂贵,因此“普通”的IO实现可以解决问题。但是,如果你正在考虑扩展到数千或更高,可能值得研究一些更复杂的东西。
java.nio包通过提供套接字多路复用/非阻塞IO来解决这个问题,它允许您将多个连接绑定到一个Selector。然而,由于多线程和非阻塞方面,这种解决方案比简单的阻塞方法更难实现。
如果您希望追求简单IO以外的东西,那么我建议您查看其中一个高质量的网络抽象库。根据个人经验,我可以推荐Netty,它可以为您完成大部分繁琐的NIO处理。然而,它确实有一点学习曲线但是一旦你习惯了基于事件的方法,它就非常强大。
答案 6 :(得分:1)
如果您对利用现有容器的部署和管理感兴趣,可以考虑在Tomcat中创建一个新的协议处理程序。请参阅this answer相关问题。
来自Matthew Schmidt的更新:This post声称Tomcat 6中基于NIO的连接器(由Filip Hanik编写)实现了16,000个并发连接。
如果您想编写自己的连接器,请查看 MINA 以帮助NIO抽象。 MINA还具有管理功能,可以消除对另一个容器的需求(如果您担心许多单元的部署及其操作等)。
答案 7 :(得分:1)
我建议它更多地依赖于服务器在处理消息时正在做什么。如果它相对轻量级,那么您的机器规格应该易于处理仅仅处理数千个这样的过程的连接。成千上万是另一个问题,但你只需要在同一个网络上的两台机器来实际测试它并获得明确的答案。
答案 8 :(得分:0)
我认为更好的方法是不自己处理线程。创建一个池(ThreadExecutor或其他一些东西)和简单的调度工作到您的池。
当然,我认为异步I / O会使它更好更快,但会帮助你解决套接字和网络问题。只要。当您的线程因I / O而阻塞时,JVM将使其进入休眠状态并更改另一个线程,直到阻塞I / O返回。但这只会阻止线程。您的处理器将继续运行并开始处理其他线程。因此,减去创建线程的时间,使用I / O的方式对模型的影响不大。 如果您不创建线程(使用池),则问题就解决了。
答案 9 :(得分:0)
为什么要自己动手?您可以使用带有servlet,消息队列或ZeroMQ的servlet容器。