换成 Undertow 后效果立竿见影:内存占用降了约29%

改完依赖、跑完压测,数据会把人拍醒。我们是把同一套 Spring Boot 服务从内置 Tomcat 换成 Undertow,然后用 JMeter 并发模拟大流量去打一个查询接口,线程数设到 1000 并发。切换前 Tomcat 的稳定 QPS 大致在 8500 左右,平均响应时间约 15ms;换成 Undertow 后 QPS 跳到 12000,平均延迟降到 8ms,95 和 99 百分位延迟也能看到明显的下行趋势。控制台启动能看到那句熟悉的日志 “Undertow started on port(s)”,这是最直接的确认。真实环境里肯定会有变动,但整体提升是超级明显的。
说清楚怎么验证:先把项目的依赖改好、参数配齐,启动起来看日志确认是 Undertow 再去压。压测的时候别只看 QPS 和平均值,要盯着 95/99 百分位,这两项能反映真实用户体验。压力测试过程里我们监控了 CPU、堆内存、网络和 GC 情况,整个过程中 Undertow 的延迟曲线更平滑,吞吐更稳定。数据对比要在同样硬件、同样测试脚本下做,别拿不同机器或不同接口的数据来比,那样没任何意义。
改动步骤实则不复杂,按顺序做三件事就行:
1) 先把 Tomcat 从构建里排掉。用 Maven 的话,在 spring-boot-starter-web 依赖里把 Tomcat 排除,再加上
spring-boot-starter-undertow;Gradle 同理,先排除 tomcat,再添加 undertow 的 starter。别把 exclude 写错位置,写错了依赖还会在运行时拉回 Tomcat,排查起来很浪费时间。
2) 配置文件里调几个核心参数。常见要改的有 IO 线程数、工作线程数、缓冲区大小,还有是否开启 HTTP/2。举个经验值:一台 8 核的机器,IO 线程可以设成 8,工作线程先从 200 开始跑压力测试看情况;缓冲区和直接内存相关的参数,按业务流量和大文件处理的需要微调。写配置时注意语法和参数名,少走低级错误。
3) 启动后走功能和性能验证。看控制台日志确认 Undertow 已经起来,跑 JMeter 测压。别忘了检查 WebSocket、分布式会话以及项目里有没有用到 Tomcat 的私有 API,这些都是常见的兼容坑。
迁移时常见的坑和注意点:
– 代码里别依赖 org.apache.catalina.* 这类 Tomcat 私有类。许多项目里某个第三方库或历史代码里会有意外引用,启动时不必定报错,但运行时可能踩坑。
– WebSocket 配置和使用方式和 Tomcat 有差别,按 Undertow 的文档去配一次,别当成“自动兼容”就完事。
– 分布式 session、cookies、session 共享要彻底跑一遍功能测试,尤其是登录态这类用户可感知的东西,避免切换后出现登录失效或会话漂移。
– 在构建脚本里排除 Tomcat、加入 Undertow 时,顺序和写法要小心,避免把依赖写反或者漏掉 scope 导致打包错误。
技术上为什么大厂更愿意选 Undertow?有几个点挺关键的:
一是 IO 模型更适合高并发。Undertow 用 XNIO 的“IO 线程 + 工作线程”分离设计:IO 线程只负责网络收发,遇到需要执行业务逻辑的请求把任务交给工作线程,做完再回池子。这样一个耗时业务不会把网络接收给卡住,新请求还能被继续接受。通俗点说,就是把“接电话”和“办事”分开,接电话的那拨人不去处理复杂业务,能一直接着电话。
二是内存和拷贝更省心。Undertow 大量使用直接内存(Direct Buffer)和零拷贝,数据在内存和网卡之间传输时减少中间拷贝次数,处理大文件或流式响应时能省不少 CPU 和内存带宽。
三是配置更灵活。线程池、缓冲区、HTTP/2、连接超时这些都能更细粒度调节,能针对不同硬件和业务场景做优化。
对比 Tomcat 的时候尤其能看出差别。Tomcat 的连接器更像传统混合模式,网络 IO、协议解析和业务执行有较多耦合。工作线程既要做 IO 又要做业务逻辑,一旦某个请求在业务层卡住,这个线程就占着,后面的请求只能在队列里等。还有内存分配上,Tomcat 启动时占用的基础内存比 Undertow 要大一些。我们测同一 Spring Boot 项目时,Tomcat 启动总体内存大约 120MB(堆大致 80MB、非堆 25MB、线程相关占约 15MB),换成 Undertow 后降到 85MB(堆 60MB、非堆 15MB、线程约 10MB),每实例节省约 35MB。把这个数字放大到 1000 个实例,就是省掉大致 35GB 内存,相当于两台 16GB 内存服务器的钱,这在大规模部署成本上很明显。
并不是所有服务都需要换。那些内部管理系统、低并发的后台工具,用 Tomcat 完全没问题,没必要为了点性能改来改去。换 Undertow 更适合对延迟敏感、并发高、或者要大规模部署的业务场景——列如电商下单、支付接口、API 网关,或者流量高峰明显的微服务。身边几个同事换了之后反馈线上故障少了,扩容也少做了,运维压力的确 下降。
最后给几条操作上的小提醒,都是实战里踩过的坑:别用 Tomcat 的私有类、WebSocket 要按 Undertow 的方式配置、分布式 session 要跑回归测试;在 build 脚本里排除和添加依赖时把位置和 scope 确认清楚。切换成功最容易确认的就是启动日志里出现 Undertow 的启动信息,接着用压测数据去对比 QPS、平均延迟和 95/99 百分位,看这些指标的趋势就清楚了。