使用 Undertow 替代 Tomcat 的优势与配置
为什么要用 Undertow 替代 Tomcat
1. 性能优势
-
更低的资源占用:Undertow 内存占用比 Tomcat 少 30-50%
-
更高的并发处理能力:基于 XNIO 的非阻塞 I/O 模型
-
更好的响应时间:轻量级架构减少处理延迟
2. 架构优势
-
模块化设计:只加载需要的组件
-
嵌入式部署:更适合微服务架构
-
灵活配置:支持编程式配置
详细优势对比
性能指标对比
| 指标 | Tomcat | Undertow | 优势 |
|---|---|---|---|
| 内存占用 | 较高 | 低 | 减少30-50% |
| 启动时间 | 较慢 | 快 | 减少40-60% |
| 并发连接 | 10k+ | 50k+ | 5倍提升 |
| CPU使用率 | 中等 | 低 | 更高效 |
技术特性对比
java
// Undertow 基于 XNIO 的优势:
// 1. 非阻塞I/O
// 2. 零拷贝技术
// 3. 直接内存访问
// 4. 更精细的线程控制
Spring Boot 中配置 Undertow
1. 添加依赖
XML
<!-- 移除 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>
<!-- 添加 Undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
2. 基础配置
java
# application.yml
server:
port: 8080
undertow:
# 线程池配置
threads:
io: 16
worker: 256
# 缓冲区配置
buffer-size: 1024
direct-buffers: true
# HTTP配置
max-http-post-size: 0
# 连接配置
max-connections: 10000
3. 高级配置示例
java
@Configuration
public class UndertowConfig {
@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
// 自定义构建器
factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Builder builder) {
// 服务器配置
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true)
.setServerOption(UndertowOptions.HTTP2_SETTINGS_ENABLE_PUSH, true)
.setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false)
.setServerOption(UndertowOptions.ALWAYS_SET_DATE, true);
// 缓冲区配置
builder.setBufferSize(1024 * 16)
.setIoThreads(Runtime.getRuntime().availableProcessors() * 2)
.setWorkerThreads(200);
}
});
// 监听器配置
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
deploymentInfo.setDefaultEncoding("UTF-8");
deploymentInfo.setUrlCharset(StandardCharsets.UTF_8.name());
});
return factory;
}
}
4. 完整的配置类
java
@Configuration
@EnableConfigurationProperties(UndertowProperties.class)
public class AdvancedUndertowConfig {
@Autowired
private UndertowProperties undertowProperties;
@Bean
@Primary
public ServletWebServerFactory servletContainer() {
UndertowServletWebServerFactory factory =
new UndertowServletWebServerFactory();
// 基础配置
factory.setPort(undertowProperties.getPort());
factory.setContextPath(undertowProperties.getContextPath());
// 高级自定义配置
factory.addBuilderCustomizers(this::customizeBuilder);
factory.addDeploymentInfoCustomizers(this::customizeDeployment);
return factory;
}
private void customizeBuilder(Builder builder) {
// XNIO 工作线程配置
builder.setWorkerThreads(undertowProperties.getWorkerThreads());
builder.setIoThreads(undertowProperties.getIoThreads());
// 缓冲区配置
builder.setBufferSize(undertowProperties.getBufferSize());
builder.setDirectBuffers(undertowProperties.isDirectBuffers());
// 连接配置
builder.setServerOption(UndertowOptions.MAX_CONNECTIONS,
undertowProperties.getMaxConnections());
builder.setServerOption(UndertowOptions.MAX_ENTITY_SIZE,
undertowProperties.getMaxEntitySize());
// HTTP/2 配置
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true);
builder.setServerOption(UndertowOptions.HTTP2_SETTINGS_HEADER_TABLE_SIZE, 4096);
builder.setServerOption(UndertowOptions.HTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 65535);
// 安全配置
builder.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, false);
builder.setServerOption(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL, true);
}
private void customizeDeployment(DeploymentInfo deploymentInfo) {
deploymentInfo.setDefaultEncoding("UTF-8");
deploymentInfo.setEagerFilterInit(true);
deploymentInfo.setIgnoreFlush(false);
// Session 配置
deploymentInfo.setDefaultSessionTimeout(1800);
}
}
// 配置属性类
@ConfigurationProperties(prefix = "server.undertow")
@Data
public class UndertowProperties {
private int port = 8080;
private String contextPath = "";
private int workerThreads = 256;
private int ioThreads = Runtime.getRuntime().availableProcessors() * 2;
private int bufferSize = 1024;
private boolean directBuffers = true;
private int maxConnections = 10000;
private long maxEntitySize = 10485760L; // 10MB
}
5. 性能优化配置
java
# application-prod.yml
server:
undertow:
threads:
worker: 500
io: 32
buffer-size: 2048
direct-buffers: true
max-http-post-size: 104857600 # 100MB
max-connections: 10000
no-request-timeout: 60000
idle-timeout: 30000
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
6. 监控和健康检查
java
@RestController
public class ServerStatusController {
@Autowired
private XnioWorker xnioWorker;
@GetMapping("/server/status")
public Map<String, Object> getServerStatus() {
Map<String, Object> status = new HashMap<>();
// 线程池状态
status.put("ioThreadCount", xnioWorker.getIoThreadCount());
status.put("workerThreadCount", xnioWorker.getWorkerThreadCount());
status.put("busyWorkerThreads", xnioWorker.getBusyWorkerThreadCount());
// 内存状态
Runtime runtime = Runtime.getRuntime();
status.put("maxMemory", runtime.maxMemory());
status.put("totalMemory", runtime.totalMemory());
status.put("freeMemory", runtime.freeMemory());
status.put("usedMemory", runtime.totalMemory() - runtime.freeMemory());
return status;
}
}
迁移注意事项
1. 会话管理
java
@Configuration
public class SessionConfig {
@Bean
public UndertowSessionCustomizer undertowSessionCustomizer() {
return deploymentInfo -> {
deploymentInfo.setSessionPersistenceManager(new FileSessionPersistence(
new File(System.getProperty("java.io.tmpdir"))
));
};
}
}
2. SSL 配置
java
server:
ssl:
key-store: classpath:keystore.p12
key-store-password: changeit
key-store-type: PKCS12
key-alias: tomcat
undertow:
ssl-contexts:
default:
enabled-protocols:
- TLSv1.2
- TLSv1.3
总结
使用 Undertow 替代 Tomcat 的主要好处:
-
性能提升:更低的延迟,更高的吞吐量
-
资源优化:减少内存占用,提高资源利用率
-
架构适配:更适合云原生和微服务架构
-
配置灵活:支持更细粒度的性能调优
在需要高性能、低延迟的应用场景中,Undertow 是比 Tomcat 更好的选择,特别是在微服务架构和云原生环境中。