内嵌服务器
问题
Spring Boot 内嵌 Web 服务器的原理是什么?如何切换和调优内嵌服务器?
答案
内嵌服务器概述
传统 Java Web 开发需要将应用打成 WAR 包部署到外部 Tomcat,Spring Boot 内嵌了 Web 服务器,应用打成可执行 JAR 即可直接运行。
支持的服务器
| 服务器 | 类型 | 特点 | 使用场景 |
|---|---|---|---|
| Tomcat | Servlet | Spring Boot 默认,社区成熟 | 通用 Web 应用 |
| Jetty | Servlet | 轻量级,适合长连接 | WebSocket 密集场景 |
| Undertow | Servlet | 高性能,低内存 | 高并发场景 |
| Netty | 非 Servlet | 异步非阻塞 | WebFlux 响应式应用 |
切换服务器
pom.xml — 切换到 Undertow
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除默认的 Tomcat -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入 Undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
内嵌服务器启动原理
关键类:
启动核心流程(简化)
// ServletWebServerApplicationContext
@Override
protected void onRefresh() {
super.onRefresh();
// 创建并启动 Web 服务器
createWebServer();
}
private void createWebServer() {
// 1. 从容器中获取 WebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 2. 创建并启动 Web 服务器
this.webServer = factory.getWebServer(getSelfInitializer());
this.webServer.start();
}
服务器调优
application.yml — Tomcat 调优
server:
port: 8080
tomcat:
# 最大工作线程数(默认 200)
threads:
max: 200
min-spare: 20
# 最大连接数(默认 8192)
max-connections: 8192
# 等待队列长度(默认 100)
accept-count: 100
# 连接超时(默认 20s)
connection-timeout: 20000
# 最大请求体大小
max-http-form-post-size: 10MB
# URI 编码
uri-encoding: UTF-8
# 开启优雅停机
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 优雅停机等待时间
线程数配置建议
- CPU 密集任务:线程数 ≈ CPU 核心数 + 1
- IO 密集任务:线程数 ≈ CPU 核心数 × 2(或更高)
- 生产环境建议通过压测确定最佳值
优雅停机
Spring Boot 2.3+ 内置优雅停机支持:
自定义 WebServerFactory
自定义 Tomcat 配置
@Component
public class TomcatCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.setPort(8080);
factory.addConnectorCustomizers(connector -> {
connector.setProperty("maxThreads", "300");
connector.setProperty("acceptCount", "200");
});
// 添加 AJP 连接器(Nginx 反向代理场景)
// factory.addAdditionalTomcatConnectors(createAjpConnector());
}
}
常见面试问题
Q1: Spring Boot 为什么使用内嵌服务器?
答案:
- 简化部署:打成可执行 JAR,
java -jar直接运行,无需安装和配置外部 Tomcat - 微服务友好:每个服务独立打包运行,适合容器化部署(Docker/K8s)
- 版本可控:服务器版本和应用一起管理,避免环境不一致问题
- 开发便捷:IDE 中直接启动 main 方法即可运行
Q2: Tomcat、Jetty、Undertow 怎么选?
答案:
- Tomcat:默认选择,社区最成熟,文档最全,适合大多数场景
- Undertow:性能好、内存占用低,适合高并发场景
- Jetty:擅长长连接(WebSocket),适合长连接密集场景
- Netty:非 Servlet 容器,专用于 WebFlux 响应式应用
一般不需要特别切换,Tomcat 能满足绝大多数需求。
Q3: 什么是优雅停机?
答案:
优雅停机是指应用收到停机信号(如 SIGTERM)后,不再接收新请求,等待正在处理的请求完成后再关闭。Spring Boot 2.3+ 通过 server.shutdown=graceful 开启,配合 spring.lifecycle.timeout-per-shutdown-phase 设置最大等待时间。这在 K8s 滚动更新时特别重要,避免用户请求被中断。
Q4: 如何修改内嵌 Tomcat 的默认端口?
答案:
# 方式1:配置文件
server.port=9090
# 方式2:命令行参数
java -jar app.jar --server.port=9090
# 方式3:代码方式(WebServerFactoryCustomizer)
设置 server.port=0 会随机分配可用端口(适合测试场景)。
相关链接
- Spring Boot 官方文档 - Web Server
- 自动配置原理 - 服务器自动配置
- 配置管理 - server 配置项
- Actuator 监控 - 健康检查与 K8s 集成