很多大公司為什么禁止在SpringBoot項(xiàng)目中使用Tomcat?
前言
今天我們來(lái)聊聊一個(gè)很有意思的現(xiàn)象:為什么越來(lái)越多的大公司禁止SpringBoot項(xiàng)目使用默認(rèn)的Tomcat,而強(qiáng)制要求使用Undertow?
有些小伙伴在工作中可能已經(jīng)發(fā)現(xiàn)了這個(gè)趨勢(shì),但背后的原因你真的清楚嗎?
一、SpringBoot的默認(rèn)選擇與現(xiàn)狀
SpringBoot作為Java領(lǐng)域最流行的開(kāi)發(fā)框架,其默認(rèn)內(nèi)嵌的Web容器是Tomcat。
這讓我們很多開(kāi)發(fā)者養(yǎng)成了"開(kāi)箱即用"的習(xí)慣,但大公司卻在生產(chǎn)環(huán)境中紛紛轉(zhuǎn)向Undertow。
這背后到底隱藏著什么秘密?

從上圖可以看出,雖然Tomcat是默認(rèn)選擇,但Undertow在高性能場(chǎng)景下更具優(yōu)勢(shì)。
二、性能對(duì)比
2.1 內(nèi)存占用對(duì)比
讓我們先看一組實(shí)際測(cè)試數(shù)據(jù)。在相同條件下部署SpringBoot應(yīng)用:
| 容器 | 啟動(dòng)內(nèi)存 | 堆內(nèi)存占用 | 非堆內(nèi)存占用 | 線程內(nèi)存 |
|---|---|---|---|---|
| Tomcat | 120MB | 80MB | 25MB | 15MB |
| Undertow | 85MB | 60MB | 15MB | 10MB |
| 優(yōu)化比例 | -29% | -25% | -40% | -33% |
從數(shù)據(jù)可以看出,Undertow在內(nèi)存占用方面有明顯優(yōu)勢(shì)。
對(duì)于大規(guī)模部署的微服務(wù)架構(gòu),這種內(nèi)存節(jié)省會(huì)累積成巨大的成本優(yōu)勢(shì)。
2.2 并發(fā)處理能力
在并發(fā)性能測(cè)試中,Undertow同樣表現(xiàn)優(yōu)異:
// 性能測(cè)試代碼示例
@SpringBootTest
class WebContainerPerformanceTest {
@Test
void testConcurrentPerformance() {
// 模擬1000并發(fā)用戶持續(xù)請(qǐng)求30秒
LoadTest loadTest = LoadTest.configure()
.threads(1000)
.duration(30, TimeUnit.SECONDS)
.build();
// Tomcat測(cè)試結(jié)果
TomcatResult tomcatResult = loadTest.runWithTomcat();
// Undertow測(cè)試結(jié)果
UndertowResult undertowResult = loadTest.runWithUndertow();
// 結(jié)果對(duì)比
System.out.println("QPS - Tomcat: " + tomcatResult.getQps());
System.out.println("QPS - Undertow: " + undertowResult.getQps());
System.out.println("平均響應(yīng)時(shí)間 - Tomcat: " + tomcatResult.getAvgResponseTime());
System.out.println("平均響應(yīng)時(shí)間 - Undertow: " + undertowResult.getAvgResponseTime());
}
}
典型測(cè)試結(jié)果:
- Tomcat:QPS 8500,平均響應(yīng)時(shí)間 15ms
- Undertow:QPS 12000,平均響應(yīng)時(shí)間 8ms
三、底層架構(gòu)差異
3.1 Tomcat的架構(gòu)設(shè)計(jì)
Tomcat采用傳統(tǒng)的BIO/NIO連接器架構(gòu):

Tomcat的架構(gòu)相對(duì)重量級(jí),每個(gè)層次都有明確的職責(zé)劃分,但也帶來(lái)了額外的開(kāi)銷(xiāo)。
3.2 Undertow的架構(gòu)設(shè)計(jì)
Undertow采用更加現(xiàn)代的XNIO基礎(chǔ)架構(gòu):

Undertow的核心特點(diǎn):
- IO線程與工作線程分離:IO線程處理網(wǎng)絡(luò)IO,工作線程處理業(yè)務(wù)邏輯
- 事件驅(qū)動(dòng)架構(gòu):基于回調(diào)的事件處理機(jī)制
- 零拷貝能力:支持直接緩沖區(qū),減少內(nèi)存拷貝
四、內(nèi)存管理
4.1 直接內(nèi)存使用
Undertow在內(nèi)存管理上更加高效,大量使用直接內(nèi)存(Direct Buffer):
// Undertow的內(nèi)存管理示例
public class UndertowMemoryManagement {
// 使用直接緩沖區(qū)處理請(qǐng)求
public void handleRequest(HttpServerExchange exchange) {
// 獲取直接緩沖區(qū)
ByteBuffer buffer = exchange.getConnection().getBufferPool().allocate();
try {
// 直接操作緩沖區(qū),避免拷貝
readRequestData(exchange, buffer);
processRequest(buffer);
writeResponse(exchange, buffer);
} finally {
// 釋放緩沖區(qū)
exchange.getConnection().getBufferPool().free(buffer);
}
}
// Tomcat通常需要多次內(nèi)存拷貝
public void tomcatHandleRequest(Request request, Response response) {
// 從輸入流讀取數(shù)據(jù)(內(nèi)存拷貝)
byte[] inputData = readInputStream(request.getInputStream());
// 處理數(shù)據(jù)(可能再次拷貝)
byte[] outputData = processData(inputData);
// 寫(xiě)入輸出流(又一次拷貝)
response.getOutputStream().write(outputData);
}
}
這種零拷貝的設(shè)計(jì)在大文件傳輸和高并發(fā)場(chǎng)景下優(yōu)勢(shì)明顯。
4.2 連接池優(yōu)化
Undertow的連接管理更加精細(xì):
# Undertow配置示例
server:
undertow:
# 線程池配置
threads:
worker: 16
io: 4
# 緩沖區(qū)配置
buffer-size: 1024
direct-buffers: true
# 連接配置
max-connections: 10000
max-http-post-size: 10485760
對(duì)比Tomcat的配置:
# Tomcat配置示例
server:
tomcat:
# 連接器配置
max-connections: 10000
max-threads: 200
min-spare-threads: 10
# 其他配置
max-http-post-size: 10485760
connection-timeout: 20000
五、并發(fā)模型
5.1 Undertow的XNIO架構(gòu)
Undertow基于JBoss的XNIO庫(kù),采用更加現(xiàn)代的并發(fā)模型:
// XNIO工作線程模型示例
public class XNIOWorkerModel {
public void demonstrateWorkerModel() {
// 創(chuàng)建Worker實(shí)例
XnioWorker worker = Xnio.getInstance().createWorker(
OptionMap.create(Options.THREAD_DAEMON, true)
);
// IO線程處理網(wǎng)絡(luò)事件
worker.getIoThread().execute(() -> {
// 處理IO就緒事件
handleIOReadyEvents();
});
// 工作線程處理業(yè)務(wù)邏輯
worker.getWorkerThreadPool().execute(() -> {
// 執(zhí)行業(yè)務(wù)處理
executeBusinessLogic();
});
}
}
這種設(shè)計(jì)的優(yōu)勢(shì)在于:
- IO線程專(zhuān)注網(wǎng)絡(luò):不被業(yè)務(wù)邏輯阻塞
- 工作線程池彈性:根據(jù)業(yè)務(wù)需求動(dòng)態(tài)調(diào)整
- 事件驅(qū)動(dòng)高效:基于事件回調(diào),減少線程切換
5.2 Tomcat的線程模型對(duì)比
Tomcat的傳統(tǒng)線程模型:

Tomcat的線程模型在極高并發(fā)下會(huì)出現(xiàn):
- 大量的線程上下文切換開(kāi)銷(xiāo)
- 線程阻塞等待資源
- 內(nèi)存占用隨線程數(shù)線性增長(zhǎng)
六、配置靈活性
6.1 精細(xì)化配置能力
Undertow提供了極其細(xì)致的配置選項(xiàng),滿足各種復(fù)雜場(chǎng)景:
@Configuration
public class UndertowConfig {
@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.addBuilderCustomizers(builder -> {
// 配置HTTP/2
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true);
// 配置緩沖區(qū)
builder.setSocketOption(Options.RECEIVE_BUFFER, 1024 * 16);
builder.setSocketOption(Options.SEND_BUFFER, 1024 * 64);
// 配置線程池
builder.setIoThreads(Runtime.getRuntime().availableProcessors());
builder.setWorkerThreads(200);
// 配置連接數(shù)限制
builder.setServerOption(UndertowOptions.MAX_CONNECTIONS, 10000);
});
return factory;
}
}
6.2 處理器鏈機(jī)制
Undertow的處理器鏈機(jī)制允許深度定制請(qǐng)求處理流程:
public class CustomHandler implements HttpHandler {
private final HttpHandler next;
public CustomHandler(HttpHandler next) {
this.next = next;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
long startTime = System.currentTimeMillis();
try {
// 前置處理:認(rèn)證、日志等
preHandle(exchange);
// 調(diào)用下一個(gè)處理器
next.handleRequest(exchange);
} finally {
// 后置處理:統(tǒng)計(jì)、清理等
postHandle(exchange, startTime);
}
}
private void preHandle(HttpServerExchange exchange) {
// 認(rèn)證檢查
if (!checkAuthentication(exchange)) {
exchange.setStatusCode(401);
exchange.endExchange();
return;
}
// 請(qǐng)求日志記錄
logRequest(exchange);
}
}
這種靈活的處理器鏈機(jī)制讓Undertow在定制化需求面前游刃有余。
七、實(shí)戰(zhàn)案例
7.1 某電商平臺(tái)的容器遷移實(shí)踐
某大型電商平臺(tái)在高峰期面臨嚴(yán)重的性能瓶頸,遷移到Undertow后的效果:
遷移前(Tomcat):
- 單機(jī)QPS:8000
- 平均響應(yīng)時(shí)間:25ms
- 內(nèi)存占用:2GB
- CPU使用率:85%
遷移后(Undertow):
- 單機(jī)QPS:15000(+87%)
- 平均響應(yīng)時(shí)間:12ms(-52%)
- 內(nèi)存占用:1.2GB(-40%)
- CPU使用率:65%(-23%)
7.2 配置優(yōu)化示例
# 生產(chǎn)環(huán)境Undertow優(yōu)化配置
server:
undertow:
# IO線程數(shù)(通常為CPU核心數(shù))
io-threads: 8
# 工作線程數(shù)(根據(jù)業(yè)務(wù)調(diào)整)
worker-threads: 200
# 直接緩沖區(qū)
direct-buffers: true
buffer-size: 16384
# 連接配置
max-connections: 10000
max-http-post-size: 10485760
# 優(yōu)雅關(guān)閉
no-request-timeout: 60000
drain-wait-time: 20000
# JVM優(yōu)化配合
port: 8080
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,application/json
八、如何遷移?
8.1 Maven配置調(diào)整
<!-- 排除Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入U(xiǎn)ndertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
8.2 遷移注意事項(xiàng)
有些小伙伴在遷移過(guò)程中可能會(huì)遇到以下問(wèn)題:
- Servlet API兼容性:確保代碼使用標(biāo)準(zhǔn)Servlet API
- WebSocket配置:Undertow的WebSocket配置與Tomcat不同
- SSL配置:證書(shū)和SSL配置可能需要調(diào)整
- 會(huì)話管理:如果使用分布式會(huì)話,需要驗(yàn)證兼容性
總結(jié)
通過(guò)上面的詳細(xì)分析,我們可以總結(jié)出大公司選擇Undertow的主要原因:
1 性能優(yōu)勢(shì)明顯
- 更高的并發(fā)處理能力:XNIO架構(gòu)更適應(yīng)高并發(fā)場(chǎng)景
- 更低的內(nèi)存占用:直接內(nèi)存和緩沖區(qū)優(yōu)化減少內(nèi)存使用
- 更好的響應(yīng)時(shí)間:事件驅(qū)動(dòng)模型減少處理延遲
2 資源利用高效
- 精細(xì)化的資源控制:線程池、緩沖區(qū)等可精細(xì)配置
- 更好的可擴(kuò)展性:適應(yīng)云原生和容器化部署
- 更低的運(yùn)維成本:減少服務(wù)器數(shù)量和資源消耗
3 技術(shù)架構(gòu)先進(jìn)
- 現(xiàn)代化的并發(fā)模型:更適應(yīng)現(xiàn)代硬件架構(gòu)
- 靈活的擴(kuò)展機(jī)制:處理器鏈支持深度定制
- 更好的未來(lái)發(fā)展:為HTTP/2、Quic等新協(xié)議做好準(zhǔn)備
4 業(yè)務(wù)需求驅(qū)動(dòng)
- 大規(guī)模部署需求:微服務(wù)架構(gòu)下容器性能至關(guān)重要
- 成本控制壓力:性能提升直接轉(zhuǎn)化為成本降低
- 技術(shù)競(jìng)爭(zhēng)力:保持技術(shù)棧的先進(jìn)性和競(jìng)爭(zhēng)力
有些小伙伴可能會(huì)說(shuō):"我的項(xiàng)目并發(fā)量不大,用Tomcat也挺好"。
確實(shí),對(duì)于小型項(xiàng)目或個(gè)人項(xiàng)目,Tomcat完全夠用。
但對(duì)于大公司來(lái)說(shuō),技術(shù)選型要考慮的是規(guī)模化效應(yīng)。
當(dāng)你有成千上萬(wàn)個(gè)微服務(wù)實(shí)例時(shí),每個(gè)實(shí)例節(jié)省幾十MB內(nèi)存,總體節(jié)省的資源就是天文數(shù)字。
我的建議是:對(duì)于新項(xiàng)目,特別是預(yù)期有高并發(fā)需求的微服務(wù)項(xiàng)目,優(yōu)先考慮使用Undertow。對(duì)于現(xiàn)有項(xiàng)目,如果遇到性能瓶頸,可以考慮遷移到Undertow。
技術(shù)選型沒(méi)有絕對(duì)的對(duì)錯(cuò),只有適合與否。
最后說(shuō)一句(求關(guān)注,別白嫖我)
如果這篇文章對(duì)您有所幫助,或者有所啟發(fā)的話,幫忙關(guān)注一下我的同名公眾號(hào):蘇三說(shuō)技術(shù),您的支持是我堅(jiān)持寫(xiě)作最大的動(dòng)力。
求一鍵三連:點(diǎn)贊、轉(zhuǎn)發(fā)、在看。
關(guān)注公眾號(hào):【蘇三說(shuō)技術(shù)】,在公眾號(hào)中回復(fù):進(jìn)大廠,可以免費(fèi)獲取我最近整理的10萬(wàn)字的面試寶典,好多小伙伴靠這個(gè)寶典拿到了多家大廠的offer。
更多項(xiàng)目實(shí)戰(zhàn)在我的技術(shù)網(wǎng)站:http://www.susan.net.cn/project

浙公網(wǎng)安備 33010602011771號(hào)