使用 Undertow 替代 Tomcat
使用 Undertow 替代 Tomcat 的优势与配置
为什么要用 Undertow 替代 Tomcat
1. 性能优势
更低的资源占用:Undertow 内存占用比 Tomcat 少 30-50%
更高的并发处理能力:基于 XNIO 的非阻塞 I/O 模型
更好的响应时间:轻量级架构减少处理延迟
2. 架构优势
模块化设计:只加载需要的组件
嵌入式部署:更适合微服务架构
灵活配置:支持编程式配置
详细优势对比
性能指标对比
| 指标 | Tomcat | Undertow | 优势 |
|---|---|---|---|
| 内存占用 | 较高 | 低 | 减少30-50% |
| 启动时间 | 较慢 | 快 | 减少40-60% |
| 并发连接 | 10k+ | 50k+ | 5倍提升 |
| CPU使用率 | 中等 | 低 | 更高效 |
技术特性对比
// Undertow 基于 XNIO 的优势:
// 1. 非阻塞I/O
// 2. 零拷贝技术
// 3. 直接内存访问
// 4. 更精细的线程控制Spring Boot 中配置 Undertow
1. 添加依赖
<!-- 移除 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. 基础配置
# application.yml
server:port: 8080undertow:# 线程池配置threads:io: 16worker: 256# 缓冲区配置buffer-size: 1024direct-buffers: true# HTTP配置max-http-post-size: 0# 连接配置max-connections: 100003. 高级配置示例
@Configuration
public class UndertowConfig {@Beanpublic UndertowServletWebServerFactory undertowServletWebServerFactory() {UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();// 自定义构建器factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {@Overridepublic 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. 完整的配置类
@Configuration
@EnableConfigurationProperties(UndertowProperties.class)
public class AdvancedUndertowConfig {@Autowiredprivate UndertowProperties undertowProperties;@Bean@Primarypublic 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. 性能优化配置
# application-prod.yml
server:undertow:threads:worker: 500io: 32buffer-size: 2048direct-buffers: truemax-http-post-size: 104857600 # 100MBmax-connections: 10000no-request-timeout: 60000idle-timeout: 30000spring:servlet:multipart:max-file-size: 100MBmax-request-size: 100MB6. 监控和健康检查
@RestController
public class ServerStatusController {@Autowiredprivate 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. 会话管理
@Configuration
public class SessionConfig {@Beanpublic UndertowSessionCustomizer undertowSessionCustomizer() {return deploymentInfo -> {deploymentInfo.setSessionPersistenceManager(new FileSessionPersistence(new File(System.getProperty("java.io.tmpdir"))));};}
}2. SSL 配置
server:ssl:key-store: classpath:keystore.p12key-store-password: changeitkey-store-type: PKCS12key-alias: tomcatundertow:ssl-contexts:default:enabled-protocols:- TLSv1.2- TLSv1.3总结
使用 Undertow 替代 Tomcat 的主要好处:
性能提升:更低的延迟,更高的吞吐量
资源优化:减少内存占用,提高资源利用率
架构适配:更适合云原生和微服务架构
配置灵活:支持更细粒度的性能调优
在需要高性能、低延迟的应用场景中,Undertow 是比 Tomcat 更好的选择,特别是在微服务架构和云原生环境中。
