我想在一小群机器(尽可能小)上支持大约 10,000 个并发 HTTP 客户端。我想在用户使用应用程序时保持与每个客户端的连接,以允许服务器推送更新。
我相信通常建议将异步 IO 用于这些类型的长期连接,以避免大量线程处于空闲状态。但是,线程闲置有什么问题?我发现线程模型在精神上更容易使用,但我不想做一些会让我头疼的事情。我想我将不得不进行实验,但我想知道是否有人知道以前有任何类似的实验?
异步 I/O 基本上意味着您的应用程序执行大部分线程调度。与其让操作系统随机挂起您的线程并计划另一个线程,不如只拥有与 CPU 内核一样多的线程,并在最合适的点(当线程达到 I/O 操作时)让出其他任务,这将需要一些时间。
从性能的角度来看,上述似乎是明显的胜利,但异步编程模型在几个方面要复杂得多:
- 它不能表示为单个函数调用,因此工作流并不明显,尤其是在考虑由于异常而导致的控制流传输时;
- 如果没有编程语言的专门针对性支持,习语就会非常混乱:意大利面条代码和/或极弱的信噪比是常态;
- 主要是由于 1. 上述调试要困难得多,因为堆栈跟踪不代表整个工作单元内的进度;
- 执行从一个池(甚至几个池,其中每个抽象层都有自己的)中的线程跳到另一个线程,因此使用常用工具进行分析和监视实际上毫无用处。
另一方面,现代操作系统发生了许多有利的改进和优化,这些改进和优化大多消除了同步I/O编程的性能缺点:
- 地址空间很大,因此为堆栈保留的空间不是问题;
- 调用堆栈的实际物理 RAM 负载不是很大,因为只有线程实际使用的堆栈部分提交到 RAM,并且调用堆栈通常不会超过 64K;
- 上下文切换曾经对于较大的线程计数来说非常昂贵,现在已经改进到其开销对于所有实际目的来说可以忽略不计的程度。
一篇经典论文涵盖了上述大部分内容和其他一些观点,是对我在这里所说的很好的补充:
https://www.usenix.org/legacy/events/hotos03/tech/full_papers/vonbehren/vonbehren_html/index.html
您的问题评论中已经有一些很好的指示。
不使用 10K 线程的原因是这会消耗内存资源,而内存会消耗能量。编程模型没有参数,因为位于客户端连接的线程不能与想要发布事件的线程相同。
请查看 Servlet 3.0 标准中的 websockets 标准和异步请求处理模型。所有最近的Java Web应用程序服务器现在都实现了它(例如Glassfish和Tomcat),它是您问题的解决方案。
问题本身无法回答,因为缺少您使用的操作系统、JVM 和应用程序服务器。但是,您可以自己快速测试它,只需使用Thread.sleep(9999999)
创建一个 servlet 或 JSP 并对其进行siege -c 10000 ...
。
> 10,000 个并发 HTTP 客户端...线程处于空闲状态有什么问题?
空闲线程的成本似乎只是为内核结构(几kb)和线程堆栈(512kb-aMB)分配的内存。但。。。
显然,你会时不时地唤醒你的n-000个线程中的每一个,对吧?这是您支付上下文切换成本的时刻,这可能不是那么小(调用系统调度程序的时间、更多的缓存未命中等)。例如,请参阅: http://www.cs.rochester.edu/u/cli/research/switch.pdf
而且您必须非常小心地固定线程,以免影响系统线程。因此,与异步 IO 相比,每个连接的线程(阻塞 IO)体系结构可能会增加系统的延迟。但是,如果几乎所有线程大部分时间都停放,它仍然可以适用于您的情况。
最后一句话。我们不知道你的线程会在 read() 上被阻塞多少次,以及它们需要做多少工作来处理接收到的数据。将使用哪些硬件、操作系统和网络接口...因此,请测试系统的原型。