科普文: Java web应用性能分析【Java性能优化:Undertow性能调优小结】
性能调优效果对比优化项默认配置优化后配置QPS提升Worker线程数CPU*8CPU*4+15%~20%直接内存缓冲区堆内存直接内存+30%HTTP/2支持关闭启用+25%响应压缩阈值无压缩>10KB启用带宽减少40%Undertow的优势:轻量级和高性能:Undertow基于非阻塞I/O模型,具有低资源消耗和高并发处理能力。支持HTTP/2和WebSockets:开箱即支持HTTP/2
概叙
科普文: Java web应用性能分析【Java性能优化:纯java编程的优化方法论小结】-CSDN博客
实战:Java web应用性能分析之【Arthas性能分析trace监控后端性能】-CSDN博客
科普文:SpringBoot项目禁用Tomcat,并用Undertow替换_springboot tomcat 禁用jsp-CSDN博客
科普文:Java web应用性能分析之【Spring Boot内嵌服务器选型:Tomcat vs Jetty vs Undertow 】_内置undertow支持并发-CSDN博客
实战:16条SpringBoot服务优化技巧_springboot优化-CSDN博客

前面总结了Undertow的优势:
- 轻量级和高性能:Undertow基于非阻塞I/O模型,具有低资源消耗和高并发处理能力。
- 支持HTTP/2和WebSockets:开箱即支持HTTP/2和WebSockets,无需重写启动类路径。
- 可嵌入式:只需几行代码即可将Undertow嵌入应用程序或独立运行。
- 灵活性:通过链式handler进行配置,可以根据需求灵活地添加功能。
性能调优效果对比
| 优化项 | 默认配置 | 优化后配置 | QPS提升 |
|---|---|---|---|
| Worker线程数 | CPU*8 | CPU*4 | +15%~20% |
| 直接内存缓冲区 | 堆内存 | 直接内存 | +30% |
| HTTP/2支持 | 关闭 | 启用 | +25% |
| 响应压缩阈值 | 无压缩 | >10KB启用 | 带宽减少40% |
注意事项
- 避免过度调优:线程数超过CPU核心数*8可能引发上下文切换问题
- 压测验证:使用JMeter/Wrk2模拟真实流量验证配置
- 版本差异:Undertow 2.x与1.x版本优化参数存在差异(如2.x默认启用HTTP/2)
这里小结一下Undertow性能调优,主要包括以下几个方面:
-
线程池配置:Undertow默认使用Tomcat的线程池配置,但可以通过自定义配置来优化性能。可以设置
server.undertow.worker-threads(处理请求的线程数)和server.undertow.io-threads(处理IO操作的线程数),根据应用的负载和系统配置进行调整。例如,可以将worker-threads设置为CPU核心数的16倍,io-threads设置为CPU核心数。 -
启用HTTP/2:HTTP/2可以在同一连接上并发多个请求,提高传输效率。在Spring Boot中启用HTTP/2只需在配置文件中设置
server.http2.enabled=true。 -
缓冲区配置:通过调整缓冲区的大小和是否使用直接内存可以提高IO性能。可以设置
server.undertow.buffer-size来指定每个缓冲区的大小,通常设置为1024或2048字节。启用直接内存可以通过设置server.undertow.direct-buffers=true来实现。 -
连接和请求超时设置:设置
server.undertow.max-http-header-size来指定最大HTTP头大小,server.undertow.max-http-post-size来设置HTTP POST请求的最大内容大小。还可以设置连接在不处理请求的情况下闲置的时间,例如server.undertow.no-request-timeout=1800s(30分钟)。 -
禁用不必要的设置:禁用Undertow中不必要的模块和功能可以减少资源消耗。例如,可以禁用不必要的Servlet容器或协议支持。
-
内存使用优化:使用堆外内存管理可以减少垃圾回收的压力。通过设置
server.undertow.direct-buffers=true来启用直接内存。
调优效果验证
- 线程池监控:通过JMX查看
XNIO Worker线程池利用率 - 内存监控:使用
jcmd <pid> VM.native_memory检查直接内存分配 - 性能压测:用
wrk或JMeter验证QPS和延迟变化
详细的Undertow配置参数
server:
undertow:
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作
# 如果每次需要 ByteBuffer 的时候都去申请,对于堆内存的 ByteBuffer 需要走 JVM 内存分配流程(TLAB -> 堆),对于直接内存则需要走系统调用,这样效率是很低下的。
# 所以,一般都会引入内存池。在这里就是 `BufferPool`。
# 目前,UnderTow 中只有一种 `DefaultByteBufferPool`,其他的实现目前没有用。
# 这个 DefaultByteBufferPool 相对于 netty 的 ByteBufArena 来说,非常简单,类似于 JVM TLAB 的机制
# 对于 bufferSize,最好和你系统的 TCP Socket Buffer 配置一样
# `/proc/sys/net/ipv4/tcp_rmem` (对于读取)
# `/proc/sys/net/ipv4/tcp_wmem` (对于写入)
# 在内存大于 128 MB 时,bufferSize 为 16 KB 减去 20 字节,这 20 字节用于协议头
buffer-size: 16364
# 是否分配的直接内存(NIO直接分配的堆外内存),这里开启,所以java启动参数需要配置下直接内存大小,减少不必要的GC
# 在内存大于 128 MB 时,默认就是使用直接内存的
directBuffers: true
threads:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个读线程和一个写线程
io: 4
# 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程
# 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
worker: 128
# http post body 大小,默认为 -1B ,即不限制
max-http-post-size: -1B
# 是否在启动时创建 filter,默认为 true,不用修改
eager-filter-init: true
# 限制路径参数数量,默认为 1000
max-parameters: 1000
# 限制 http header 数量,默认为 200
max-headers: 200
# 限制 http header 中 cookies 的键值对数量,默认为 200
max-cookies: 200
# 是否允许 / 与 %2F 转义。/ 是 URL 保留字,除非你的应用明确需要,否则不要开启这个转义,默认为 false
allow-encoded-slash: false
# 是否允许 URL 解码,默认为 true,除了 %2F 其他的都会处理
decode-url: true
# url 字符编码集,默认是 utf-8
url-charset: utf-8
# 响应的 http header 是否会加上 'Connection: keep-alive',默认为 true
always-set-keep-alive: true
# 请求超时,默认是不超时,我们的微服务因为可能有长时间的定时任务,所以不做服务端超时,都用客户端超时,所以我们保持这个默认配置
no-request-timeout: -1
# 是否在跳转的时候保持 path,默认是关闭的,一般不用配置
preserve-path-on-forward: false
options:
# spring boot 没有抽象的 xnio 相关配置在这里配置,对应 org.xnio.Options 类
socket:
SSL_ENABLED: false
# spring boot 没有抽象的 undertow 相关配置在这里配置,对应 io.undertow.UndertowOptions 类
server:
ALLOW_UNKNOWN_PROTOCOLS: false
参数映射对照表
以下是 Undertow 性能优化参数与 Spring Boot server.undertow.* 配置属性的对应关系。
| Undertow.builder() 配置项 | Spring Boot 配置参数(application.yml) | 说明 |
|---|---|---|
setWorkerThreads(int) |
server.undertow.threads.worker |
Worker 线程池大小(默认=CPU核心数*8) |
setIoThreads(int) |
server.undertow.threads.io |
I/O 线程数(默认=CPU核心数) |
setBufferSize(int) |
server.undertow.buffer-size |
缓冲区大小(默认=16KB) |
setSocketOption(Options.READ_TIMEOUT) |
server.undertow.socket.read-timeout |
读取超时时间(毫秒) |
setSocketOption(Options.WRITE_TIMEOUT) |
server.undertow.socket.write-timeout |
写入超时时间(毫秒) |
setServerOption(DIRECT_BUFFERS, true) |
server.undertow.direct-buffers |
启用直接内存(默认=true) |
setServerOption(ENABLE_HTTP2, true) |
server.undertow.http2.enabled |
启用HTTP/2(默认=true) |
setServerOption(MAX_KEEP_ALIVE_REQUESTS) |
server.undertow.max-keep-alive-requests |
单个连接最大请求数(默认=无限) |
setServerOption(IDLE_TIMEOUT) |
server.undertow.no-request-timeout |
关键配置对比
| 参数类型 | YAML 配置项 | 编程配置项 |
|---|---|---|
| 基础参数 | server.undertow.threads.* |
无需编程 |
| 高级选项 | server.undertow.options.server |
UndertowBuilderCustomizer |
| 协议级优化 | server.undertow.http2 |
无需编程 |
通过此配置,可显著提升Undertow在高并发场景下的吞吐量(实测QPS提升30%~50%)及稳定性。
显示编程配置:无法通过 YAML 配置时
import io.undertow.UndertowOptions;
import org.springframework.boot.web.embedded.undertow.UndertowBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UndertowConfig {
@Bean
public UndertowBuilderCustomizer undertowCustomizer() {
return builder -> {
// 禁用阻塞任务标记
builder.setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false);
// 启用管道化缓冲区
builder.setSocketOption(Options.BUFFER_PIPELINED_DATA, true);
// 禁用请求耗时统计
builder.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, false);
};
}
}
一、核心线程模型优化
Worker 线程池配置
Undertow.builder()
.setWorkerThreads(ioThreads * 4) // 默认= ioThreads*8,建议= CPU核心数*2~4
.setIoThreads(Runtime.getRuntime().availableProcessors()) // 默认=CPU核心数
- I/O 线程:处理非阻塞I/O事件,数量通常等于CPU核心数,避免过多上下文切换
- Worker 线程:处理业务逻辑,建议设置为I/O线程的2-4倍(根据业务阻塞情况调整)
禁用阻塞任务标记
.setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false) // 避免自动标记阻塞
二、缓冲区与内存管理
直接内存优化
.setServerOption(UndertowOptions.DIRECT_BUFFERS, true) // 启用直接内存
.setSocketOption(Options.BUFFER_PIPELINED_DATA, true) // 启用管道化缓冲区
- 通过
-XX:MaxDirectMemorySize控制直接内存上限,避免OOM
缓冲区大小调整
.setBufferSize(1024 * 16) // 默认=16KB,大文件传输可调至64KB~128KB
.setIoThreadSubmissions(10000) // 高并发时增大队列容量
三、连接与请求处理优化
连接超时控制
.setSocketOption(Options.READ_TIMEOUT, 30_000)
.setSocketOption(Options.WRITE_TIMEOUT, 30_000)
- 防止慢连接占用资源,建议设置30~60秒超时
Keep-Alive 控制
.setServerOption(UndertowOptions.MAX_KEEP_ALIVE_REQUESTS, 100) // 单个连接最大请求数
.setServerOption(UndertowOptions.IDLE_TIMEOUT, 60_000) // 空闲连接超时
四、服务器配置增强
禁用非必要功能
.setServerOption(UndertowOptions.ENABLE_HTTP2, true) // 启用HTTP/2
.setServerOption(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL, false)
.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, false) // 关闭请求耗时统计
响应压缩优化
.addEncodingHandler("gzip", new GzipEncodingProvider(), 50, Predicates.parse("max-content-size=10000"))
- 对大于10KB的响应启用Gzip压缩,降低网络传输耗时
五、高级调优技巧
零拷贝技术
.setHandler(new HttpHandler() {
public void handleRequest(HttpServerExchange exchange) {
exchange.setPersistent(false); // 禁用持久化连接复用
exchange.dispatch(SameThreadExecutor.INSTANCE, () -> {...}); // 避免线程切换
}
})
热部署优化
.setServerOption(UndertowOptions.ALLOW_OPTIMIZED_CONTENT_LENGTH, true)
.setServerOption(UndertowOptions.ALLOW_BLOCKING_IO_INTERRUPT, false)
六、监控与诊断
启用JMX监控
-Dorg.jboss.byteman.verbose -Dorg.jboss.logging.provider=slf4j
- 监控
XNIO Worker线程池队列堆积情况25
内存泄漏检测
.setServerOption(UndertowOptions.ENABLE_CONNECTOR_STATISTICS, true) // 开启连接统计
- 配合
jcmd <pid> VM.native_memory分析直接内存泄漏
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)