跳到主要内容

内嵌服务器

问题​

Spring Boot 内嵌 Web 服务器的原理是什么?如何切换和调优内嵌服务器?

答案​

内嵌服务器概述​

传统 Java Web 开发需要将应用打成 WAR 包部署到外部 Tomcat,Spring Boot 内嵌了 Web 服务器,应用打成可执行 JAR 即可直接运行。

支持的服务器​

服务器类型特点使用场景
TomcatServletSpring Boot 默认,社区成熟通用 Web 应用
JettyServlet轻量级,适合长连接WebSocket 密集场景
UndertowServlet高性能,低内存高并发场景
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 为什么使用内嵌服务器?​

答案:

  1. 简化部署:打成可执行 JAR,java -jar 直接运行,无需安装和配置外部 Tomcat
  2. 微服务友好:每个服务独立打包运行,适合容器化部署(Docker/K8s)
  3. 版本可控:服务器版本和应用一起管理,避免环境不一致问题
  4. 开发便捷: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 会随机分配可用端口(适合测试场景)。

相关链接​