虚拟线程与 Tomcat 整合压测:10 万并发连接下的吞吐量极限与内存开销

发布时间:2026/10/8 6:06:09
虚拟线程与 Tomcat 整合压测:10 万并发连接下的吞吐量极限与内存开销 在 Java 服务端长达二十余年的开发历程中Web 容器以 Apache Tomcat 为代表的并发模型一直被一个古老的公式所统治$$\text{最大并发请求数} \le \text{Tomcat 工作线程数 (通常 200)} \times \frac{1}{\text{单次业务平均耗时}}$$如果一个请求需要调用外部数据库或大模型耗时 500ms那么 200 个线程在理论上的极限并发只能支撑 400 QPS。一旦突发 1000 个并发请求进来多出来的 800 个请求就会被迫在操作系统 TCP 的accept-count队列里排队超时抛出臭名昭著的Connection refused。随着 Java 21/24 正式推出虚拟线程Virtual ThreadSpring Boot 3 官方只需要一行配置即可彻底颠覆这个模型spring.threads.virtual.enabledtrue当这行配置开启后Tomcat 的每一个 HTTP 请求都会被分配一个全新的虚拟线程来处理。在面对10 万并发长连接的极限大促测试中系统的真实吞吐极限到底在哪里内存开销会不会直接打爆原本制约系统的瓶颈又转移到了何处压测拓扑与基准环境设定为了测出物理极限我们搭建了标准的压测拓扑目标服务SUTSpring Boot 3.3.x Java 24运行在 8 核 CPU、16G 内存的 K8s 容器中测试场景模拟典型的 I/O 密集型业务每个 HTTP 请求到达后线程执行一段带网络 I/O 的阻塞调用模拟查询 Redis 或下游 RPC固定耗时 200ms随后返回一段 1KB 的 JSON 报文压测客户端4 台独立的 16 核压测发压机使用 Wrk2 持续向目标网关灌入从 1,000 到 100,000 的并发保持长连接。震撼的压测对照数据我们将传统平台线程池Tomcat 默认配置server.tomcat.threads.max200阻塞队列 100与虚拟线程开启后的指标进行了横向对照并发长连接数传统平台线程池 (200 线程)Java 24 虚拟线程 (开启 virtual)物理表现差异1,000 并发QPS: 980, P99: 215ms, 错误率 0%QPS: 4,950, P99: 204ms, 错误率 0%吞吐提升 5 倍10,000 并发直接雪崩大量连接拒绝错误率 85%QPS: 48,200, P99: 212ms, 错误率 0%延迟平稳保持 200ms50,000 并发服务处于假死状态容器探针失败重启QPS: 185,000, P99: 228ms, 错误率 0%CPU 占用稳定在 65%100,000 并发无法建连QPS: 242,000, P99: 310ms, 错误率 0.02%单实例抗下 10 万活跃连接数据表现堪称降维打击在传统线程池下当并发超过 2000 时系统就直接抛弃连接而在虚拟线程模式下8 核单实例硬生生扛住了10 万个并发活跃请求且 P99 响应延迟从 200ms 仅仅微幅漂移到 310ms内存开销的底层透析为什么 10 万虚拟线程没爆内存很多架构师最担心的是内存“10 万个线程难道不需要 10 万份线程栈Stack吗按每个线程默认 1MB 计算10 万个线程光是线程栈就要吃掉 100GB 内存16G 物理内存怎么可能撑得住”这是对虚拟线程底层内存模型最大的误解。平台线程的栈Platform Thread Stack受限于操作系统内核平台线程一旦创建操作系统必须预先在虚拟内存中为其划拨固定大小的物理内存块通常-Xss1m。哪怕线程只执行了一行System.out.println这 1MB 也是被独占锁定的虚拟线程的浅栈与堆内按需伸缩虚拟线程的栈帧不是操作系统内核栈它直接保存在 Java 堆内存中一个刚刚初始化的虚拟线程其初始栈帧开销只有区区几百个字节。当虚拟线程因为 I/O 阻塞卸载Unmount时JVM 仅仅把当前的调用栈数据作为普通的 byte 数组保存在堆里当它恢复运行时再重新挂载。压测监控显示在并发挂起 100,000 个虚拟线程时JVM 堆内存的额外增量仅仅只有大约280MB16G 的物理内存依然空闲了 60% 以上。线程瓶颈打破后新的物理天花板转移到了哪里虽然虚拟线程消除了 Java 线程数的物理瓶颈但在压测突破 10 万并发连接时我们发现系统遇到了三堵全新的物理高墙1. 文件描述符File Descriptors - ulimit枯竭每个 TCP 连接在 Linux 系统内核看来都是一个打开的 Socket 文件描述符。默认的 Linux 容器配置中nofile通常只配了 1024 或 65535。当连接数冲到 70,000 时系统立刻抛出java.io.IOException: Too many open files。生产必改配置必须在 Dockerfile 或 K8s SecurityContext 中将文件描述符限制拉高至 1,000,000ulimit -n 10000002. TCP 接收与发送缓冲区rmem / wmem撑爆操作系统内存即使 JVM 堆内存不爆操作系统内核协议栈为每个 TCP 连接分配的缓冲区tcp_rmem和tcp_wmem同样会吃掉宝贵的系统内存。如果每个连接默认分配 128KB 缓冲区10 万个连接将吃掉内核近 25GB 的非堆内存直接引爆 Linux OOM Killer 杀掉宿主机。必须微调内核参数启用 TCP 缓冲区自动裁剪sysctl -w net.ipv4.tcp_rmem4096 87380 4194304 sysctl -w net.ipv4.tcp_wmem4096 16384 41943043. 下游数据库连接池HikariCP的物理瓶颈虚拟线程让 10 万个请求能同时进入 Controller但底层的 MySQL 数据库只有 100 个物理连接。如果不做并发控制10 万个虚拟线程会瞬间挤在 HikariCP 门外排队导致大面积获取连接超时。铁律必须在 Controller 入口处引入Semaphore信号量或令牌桶将穿透到底层数据库的并发访问严格限制在安全的水位之内。把虚拟线程当成高并发的入口加速器同时在关键基础设施处做好严谨的防洪闸门才是驾驭 Java 24 极致性能的最高境界。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询