SpingBoot之替换容器为Undertow
Undertow 是一个采用 Java 开发的灵活的高性能Web服务器,提供包括阻塞和基于NIO的非堵塞机制。Undertow 是红帽公司的开源产品,是 Wildfly 默认的 Web 服务器。Undertow 提供一个基础的架构用来构建 Web 服务器,这是一个完全为嵌入式设计的项目,提供易用的构建器 API,完全向下兼容和低级非堵塞的处理器。...

什么是Undertow容器
Undertow 是一个采用 Java 开发的灵活的高性能Web服务器,提供包括阻塞和基于 NIO 的非堵塞机制。Undertow 是红帽公司的开源产品,是 Wildfly 默认的 Web 服务器。Undertow 提供一个基础的架构用来构建 Web 服务器,这是一个完全为嵌入式设计的项目,提供易用的构建器 API,完全向下兼容 Java EE Servlet 3.1 和低级非堵塞的处理器。
为什么使用Undertow容器
为什么要替换Undertow容器?
Tomcat默认的最大线程为200,线程数限制了请求调用的性能;在高并发场景下明显并发能力较弱,需要进行调优。
为什么不进行Tomcat参数调优?
有两个方案:进行Tomcat容器调优或者替换为性能更强的容器。可以进行Tomcat调优,但是Undertow容器在性能和内存上都优于Tomcat容器,因此直接选择Undertow容器并进行调优是更好的选择。
其实在并发量不大的情况下 Undertow、和其它两款 Servlet Web 容器 Jetty 、Tomcat 的差距并不是很大。 Undertow 的优势是高并发下的吞吐量,你可以根据自己的实际需要来选择。
SpringBoot内嵌容器支持Tomcat(默认)、Jetty、Undertow。为什么选择Undertow?

Undertow的特性
Undertow 是红帽公司开发的一款基于 NIO 的高性能 Web 嵌入式服务器,有以下特性:
-
支持HTTP 2.0:Undertow 支持 HTTP/2 开箱即用,不需要重写引导类路径。
-
支持HTTP升级:支持 HTTP 升级,允许多个协议通过 HTTP 端口上进行复用。
-
支持WebSocket:Undertow 提供对 Web 套接字的全面支持,包括对 JSR-356 的支持。
-
支持Servlet 4.0:Undertow 提供了对 Servlet 4.0 的支持,包括对嵌入式 Servlet 的支持,还可以混合部署 Servlet 和原生 Undertow 非阻塞处理程序。
-
内嵌性:Undertow 可以嵌入到应用程序中,它不需要容器,只需通过 API 即可快速搭建 Web 服务器。
-
轻量级:它是一个内嵌Web服务器,仅由两个核心jar包组成,加载一个 Web 应用可以小于 10MB 内存。
-
高灵活性:一个 Undertow 服务器是通过链式处理器来配置的,可以根据需要添加功能,因此可以避免添加没有必要的功能。
集成Undertow容器
1. 添加依赖
在依赖中排除默认的tomcat容器依赖,添加undertow容器依赖:
<!--web 模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--排除tomcat依赖 -->
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<!--undertow容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
2. 编写Undertow配置
创建undertow.properties配置文件
# Undertow 日志存放目录
server.undertow.accesslog.dir=./undertow-logs
# 是否启动日志
server.undertow.accesslog.enabled=false
# 日志格式
server.undertow.accesslog.pattern=common
# 日志文件名前缀
server.undertow.accesslog.prefix=access_log
# 日志文件名后缀
server.undertow.accesslog.suffix=log
# HTTP POST请求最大的大小
server.undertow.max-http-post-size=0
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
server.undertow.io-threads=4
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
server.undertow.worker-threads=20
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
server.undertow.buffer-size=1024
# 每个区分配的buffer数量 , 所以pool的大小是buffer-size * buffers-per-region(已弃用)
#server.undertow.buffers-per-region=1024
# 是否分配的直接内存
server.undertow.direct-buffers=true
下面是yaml格式的配置:
server:
port: 9020
http2:
enabled: true
undertow:
# io-threads:IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接,默认设置每个CPU核心一个线程,不可设置过大,否则启动项目会报错:打开文件数过多。
# worker-threads:阻塞任务线程池,当执行类似servlet请求阻塞IO操作,undertow会从这个线程池中取得线程。它的值取决于系统线程执行任务的阻塞系数,默认值是 io-threads*8
threads:
io: 16
worker: 256
# 以下配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理。
# buffer-size:每块buffer的空间大小,越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可
# buffers-per-region:每个区分配的buffer数量,所以pool的大小是buffer-size * buffers-per-region
# direct-buffers:是否分配的直接内存(NIO直接分配的堆外内存)
buffer-size: 1024
direct-buffers: true
参数解释:
-
io-threads:IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接,默认设置每个CPU核心一个线程,不可设置过大,否则启动项目会报错:打开文件数过多。
-
worker-threads:阻塞任务线程池,当执行类似servlet请求阻塞IO操作,undertow会从这个线程池中取得线程。它的值取决于系统线程执行任务的阻塞系数,默认值是 io-threads*8
-
buffer-size:每块buffer的空间大小,越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可
-
buffers-per-region:每个区分配的buffer数量,所以pool的大小是buffer-size * buffers-per-region
-
direct-buffers:是否分配的直接内存(NIO直接分配的堆外内存)
3. 启动测试
Undertow启动成功提示语
[INFO ] 2020-08-13 10:38:32 [main] o.s.b.w.e.u.UndertowServletWebServer - Undertow started on port(s) 80 (http) with context path ''
复制代码
Tomcat启动成功提示语
[INFO ] 2020-08-13 10:41:35 [main] o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 80 (http) with context path ''
出现undertow的提示语,则说明springboot默认启动容器已经替换为undertow容器。
Undertow的FileServer
除了采用 NIO 的方式异步处理请求,undertow还有文件服务器的功能:
import java.io.File;
import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.server.handlers.resource.PathResourceManager;
public class FileServer {
public static void main(String[] args) {
File file = new File("/");
Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
.setHandler(Handlers.resource(new PathResourceManager(file.toPath(), 100))
.setDirectoryListingEnabled(true))
.build();
server.start();
}
}
SpringBoot下比较Tomcat与Undertow的性能
1. 添加依赖
pom.xml配置
如果使用tomcat服务器,则配置如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
复制代码
如果使用undertow服务器,则配置如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 若使用log4j2 -->
<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. 创建容器配置文件
定制tomcat/undertow服务器相关配置:
tomcat配置
/**
*
*/
package com.lz.ovuola.general.util.tomcat;
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 编程方式自定义内嵌容器
*
* @author deepinsea
*
*/
@Configuration
@ConfigurationProperties(prefix = "tomcat")
public class CustomTomcatEmbeddedCustomizer {
private int maxThreads;
private int minSpareThreads;
private int acceptCount;
private int connectionTimeout;
private String URIEncoding = "UTF-8";
private boolean disableUploadTimeout;
private boolean enableLookups;
private String compression;
private int compressionMinSize;
private String compressableMimeType;
/**
* 订制内嵌tomcat容器
*/
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());
return factory;
}
class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
public void customize(Connector connector) {
Http11NioProtocol protocol = (Http11NioProtocol) connector
.getProtocolHandler();
// 设置最大连接数
protocol.setMaxThreads(maxThreads);
protocol.setConnectionTimeout(connectionTimeout);
protocol.setMinSpareThreads(minSpareThreads);
protocol.setAcceptorThreadCount(acceptCount);
protocol.setDisableUploadTimeout(disableUploadTimeout);
protocol.setCompression(compression);
protocol.setCompressionMinSize(compressionMinSize);
protocol.setCompressableMimeType(compressableMimeType);
// connector.setURIEncoding(URIEncoding);
connector.setEnableLookups(enableLookups);
}
}
public int getMaxThreads() {
return maxThreads;
}
public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
}
public int getMinSpareThreads() {
return minSpareThreads;
}
public void setMinSpareThreads(int minSpareThreads) {
this.minSpareThreads = minSpareThreads;
}
public int getAcceptCount() {
return acceptCount;
}
public void setAcceptCount(int acceptCount) {
this.acceptCount = acceptCount;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public String getURIEncoding() {
return URIEncoding;
}
public void setURIEncoding(String uRIEncoding) {
URIEncoding = uRIEncoding;
}
public boolean isDisableUploadTimeout() {
return disableUploadTimeout;
}
public void setDisableUploadTimeout(boolean disableUploadTimeout) {
this.disableUploadTimeout = disableUploadTimeout;
}
public boolean isEnableLookups() {
return enableLookups;
}
public void setEnableLookups(boolean enableLookups) {
this.enableLookups = enableLookups;
}
public String getCompression() {
return compression;
}
public void setCompression(String compression) {
this.compression = compression;
}
public int getCompressionMinSize() {
return compressionMinSize;
}
public void setCompressionMinSize(int compressionMinSize) {
this.compressionMinSize = compressionMinSize;
}
public String getCompressableMimeType() {
return compressableMimeType;
}
public void setCompressableMimeType(String compressableMimeType) {
this.compressableMimeType = compressableMimeType;
}
}
或者是 undertow,测试只要启动一个就行:
undertow配置
//package com.lz.ovuola.general.util.tomcat;
//
//import io.undertow.Undertow.Builder;
//
//import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
//import org.springframework.boot.context.embedded.undertow.UndertowBuilderCustomizer;
//import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
//import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
//@Configuration
//public class CustomUndertowEmbeddedCustomizer {
//
// @Bean
// public EmbeddedServletContainerFactory servletContainer() {
// UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
// factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
//
// @Override
// public void customize(Builder builder) {
// builder.addHttpListener(8080, "127.0.0.1");
// }
//
// });
// return factory;
// }
//
// }
3. 添加启动配置参数
在application -runAs -run as configuratuion-Arguments添加:--用于jconsole或者是visualVM监控,推荐使用后者:
-Djava.rmi.server.hostname=127.0.0.1 --ip地址
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port="9093" --端口号
-Dcom.sun.management.jmxremote.authenticate="false"
4. 使用VisualVM监控
采用visualVM监控同一个服务,分别开启tomcat/undertow容器,注意两者在application.propertites参数尽量相同,以便观察稳定性
5. 使用Jmeter压测性能
打开jemter压力测试某一接口,观察堆内存、线程数、cpu等指标。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)