跳到主要内容

内嵌服务器

问题

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 会随机分配可用端口(适合测试场景)。

相关链接