【问题标题】:Threads sitting idle = bad?线程闲置=不好?
【发布时间】:2014-03-04 21:04:54
【问题描述】:

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

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

【问题讨论】:

  • 真正的问题在于 10k 线程本身的分叉。这将使用大量的堆栈空间来执行此操作。如果你有内存,那么它可能对你有用,但 NIO 解决方案更好,尽管更复杂。
  • 这也是 10k 个不同的 HTTP 服务器吗?如果没有,您可能需要考虑使用 HTTP keepalive 并重用现有连接
  • @Gray 你对典型线程的实际堆栈使用情况有估计吗?最大堆栈大小无关紧要,因为完整大小并未提交给物理 RAM。
  • @CodeChimp 正确配置为服务器级机器的 Linux 可以定期支持数百万的连接。

标签: java multithreading asynchronous nio c10k


【解决方案1】:

异步 ​​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

【讨论】:

    【解决方案2】:

    在您的问题的 cmets 中已经有一些很好的指示。

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

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

    由于您使用的操作系统、JVM 和应用程序服务器丢失,问题本身无法回答。但是,您可以自己快速测试它,只需使用Thread.sleep(9999999) 创建一个servlet 或JSP 并在其上执行siege -c 10000 ...

    【讨论】:

    • “内存消耗能量”?这是什么意思?
    • 正如我所说,你放入的内存越多,内存越快,服务器的功耗就越高。
    • 另一个建议:你有没有看过 Servlet 3.0 中的异步请求处理?
    • 每根棍子 1-2 瓦 (Ddr3),如果因为停车而没有人使用它
    • 您能否提供一些参考资料表明内存在系统功率中占很大比例?
    【解决方案3】:

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

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

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

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

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

    【讨论】:

    • @cruftex 但你要唤醒线程吗? :) 如果没有,就打断它
    猜你喜欢
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 2020-06-11
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    相关资源
    最近更新 更多