12

我想在一个小型机器集群(尽可能小)上同时支持大约 10,000 个 HTTP 客户端。我想在用户使用应用程序时保持与每个客户端的连接,以允许服务器推送更新。

我相信通常建议将异步 IO 用于这些类型的长期连接,以避免大量线程处于空闲状态。但是让线程闲置有什么问题呢?我发现线程模型在精神上更容易使用,但我不想做会让我头疼的事情。我想我必须进行实验,但我想知道是否有人知道以前的这些实验?

4

3 回答 3

4

异步 I/O 基本上意味着您的应用程序执行大部分线程调度。与其让操作系统随机挂起您的线程并安排另一个线程,不如让您拥有与 CPU 内核数量一样多的线程,并在最合适的点让步给其他任务——当线程到达 I/O 操作时,这将花费一些时间时间。

从性能的角度来看,上述内容似乎是一个明显的胜利,但异步编程模型在以下几个方面要复杂得多:

  1. 不能表示为单个函数调用,因此工作流程不明显,特别是考虑到异常引起的控制流转移时;
  2. 没有来自编程语言的特别有针对性的支持,惯用语非常混乱:意大利面条代码和/或极弱的信噪比是常态;
  3. 主要是由于 1. 上述调试要困难得多,因为堆栈跟踪并不代表整个工作单元内的进度;
  4. 执行在一个池(甚至几个池,其中每个抽象层都有自己的抽象层)内从一个线程跳到另一个线程,因此使用常用工具进行分析和监视实际上是无用的。

另一方面,现代操作系统发生了许多有利的改进和优化,这些改进和优化基本上消除了同步 I/O 编程的性能缺点:

  • 地址空间很大,因此为堆栈保留的空间不是问题;
  • 调用堆栈的实际物理 RAM 负载不是很大,因为只有线程实际使用的堆栈部分被提交到 RAM,调用堆栈通常不超过 64K;
  • 上下文切换过去对于较大的线程数来说非常昂贵,现在已经改进到其开销对于所有实际目的都可以忽略不计的程度。

一篇经过上述大部分内容和其他一些观点的经典论文是对我在这里所说的内容的一个很好的补充:

https://www.usenix.org/legacy/events/hotos03/tech/full_papers/vonbehren/vonbehren_html/index.html

于 2014-03-06T12:02:44.707 回答
2

您的问题的评论中已经有一些很好的指示。

不使用 10K 线程的原因是这会消耗内存资源,而内存会消耗能量。编程模型是没有争议的,因为坐在客户端连接上的线程不能与想要发布事件的线程相同。

请看一下 websockets 标准和 Servlet 3.0 标准中的异步请求处理模型。所有最近的 Java Web 应用程序服务器现在都实现了它(例如 Glassfish 和 Tomcat),它是您问题的解决方案。

由于缺少您使用的操作系统、JVM 和应用程序服务器,因此无法回答问题本身。但是,您可以自己快速测试它,只需创建一个 servlet 或 JSPThread.sleep(9999999)siege -c 10000 ...对其进行操作。

于 2014-03-04T21:50:02.417 回答
1

10,000 个并发 HTTP 客户端......让线程闲置有什么问题?

似乎空闲线程的成本只是为内核结构(几kb)和线程堆栈(512kb-若干mb)分配的内存。但...

显然,你会不时地唤醒你的每一个 n-000 个线程,对吧?那是您支付上下文切换成本的时刻,这可能不是那么小(调用系统调度程序的时间,更多的缓存未命中等)。参见,例如: http ://www.cs.rochester.edu/u/cli/research/switch.pdf

而且您必须非常小心地固定线程,以免影响系统线程。因此,与异步 IO 相比,每个连接线程(阻塞 IO)架构会增加系统的延迟。但是,如果大部分时间几乎所有线程都处于停顿状态,它仍然适用于您的情况。

最后一句话。我们不知道您的线程将在 read() 上被阻塞多少次,以及他们需要做多少工作来处理接收到的数据。将使用哪些硬件、操作系统和网络接口……所以,测试您的系统原型。

于 2014-03-04T22:20:12.120 回答