spring WebFlux 是 Spring 5 引入的响应式 Web 框架,它支持非阻塞、事件驱动的编程模型,特别适合处理高并发的场景。
Spring WebFlux 可以运行在多种容器上
包括下面:
-
Netty:
-
Netty 是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。Spring WebFlux 默认使用 Netty 作为其服务器容器。
-
-
Undertow:
-
Undertow 是一个用 Java 编写的灵活的高性能 Web 服务器,它提供了基于 NIO 的非阻塞 HTTP 和 WebSocket 支持。Spring Boot 支持将 Undertow 作为其嵌入式服务器之一。
-
-
Tomcat:
-
Tomcat 是一个广泛使用的 Servlet 容器,支持传统的 Servlet 和 JSP 技术。从 Spring Boot 2.0 开始,Tomcat 也支持非阻塞 I/O,因此可以与 Spring WebFlux 一起使用。
-
-
Jetty:
-
Jetty 是一个灵活的、轻量级的 Web 服务器和 Servlet 引擎,它也支持非阻塞 I/O。Spring Boot 同样支持将 Jetty 作为其嵌入式服务器之一。
-
在选择容器时,需要考虑应用程序的需求和容器的特性。例如,如果你的应用程序需要处理大量的并发连接,那么 Netty 或 Undertow 可能是更好的选择,因为它们在设计上就是非阻塞的,可以更有效地利用系统资源。而 Tomcat 和 Jetty 虽然也支持非阻塞 I/O,但它们最初是为阻塞 I/O 设计的,因此在某些场景下可能不如 Netty 和 Undertow 高效。
使用Netty 容器
要在 Spring WebFlux 中使用 Netty 容器,你不需要显式地配置任何依赖,因为 Spring Boot 的 spring-boot-starter-webflux
启动器已经包含了所需的 Netty 依赖。当你创建一个新的 Spring WebFlux 项目时,只需添加 spring-boot-starter-webflux
依赖,Spring Boot 会自动配置并使用 Netty 作为服务器容器。
添加依赖:
Maven 示例 (pom.xml
):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
编写配置类
mport org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class NettyConfig {
@Bean
public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> customizer() {
return factory -> {
// 自定义Netty参数
factory.addServerCustomizers(builder ->
builder
// 设置工作线程池大小
.workerEventLoopGroup(NettyThreadFactory.create("netty-worker", 4))
// 设置监听线程池大小
.bossEventLoopGroup(NettyThreadFactory.create("netty-boss", 1))
);
};
}
}
编写响应式控制器:
创建一个响应式控制器来处理 HTTP 请求。Spring WebFlux 使用 @RestController
和 @RequestMapping
等注解,但也可以使用函数式端点。
示例控制器 (@RestController
):
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class DemoController {
@GetMapping("/demo")
public Mono<String> demo() {
return Mono.just(" I am a Demo!");
}
}
函数式端点 (RouterFunction
):
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@Configuration
public class DemoRouter {
@Bean
public RouterFunction<ServerResponse> routeHello(DemoHandler demoHandler) {
return route(GET("/demo"), demoHandler::demo);
}
}
处理程序类
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class DemoHandler {
public Mono<ServerResponse> demo(ServerRequest request) {
return ServerResponse.ok().body(Mono.just("I am a Demo!"), String.class);
}
}
运行这个应用程序时,Spring Boot 将自动使用 Netty 作为服务器容器,并在默认端口(通常是 8080)上启动服务。你可以通过访问 http://localhost:8080/demo
来测试你的响应式控制器。
使用Undertow容器
Undertown 的优势如下:
- 支持 HTTP/2:Undertow 开箱即支持 HTTP/2,无需重写启动类路径。
- 支持 HTTP Upgrade:允许通过 HTTP 端口复用多种协议。
- 支持 Web Socket:Undertow 提供对 Web Sockets 的全面支持,包括 JSR-356 支持。
- Servlet 4.0:Undertow 支持 Servlet 4.0,包括对嵌入式 Servlet 的支持。还可以在同一部署中混合使用 Servlet 和原生 undertow 非阻塞 handler。
- 可嵌入式:只需几行代码,即可将 Undertow 嵌入应用程序或独立运行。
- 灵活性:Undertow 通过链式 handler 进行配置,可以根据需求灵活地添加功能。
要在 Spring Boot 应用程序中使用 Undertow 作为服务器容器,你需要在项目的依赖中指定 spring-boot-starter-web
而不是 spring-boot-starter-webflux
,因为 Undertow 支持传统的 Servlet 3.1 非阻塞 I/O,同时也支持 Spring MVC。
添加依赖
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
配置Undertow
你可以在application.properties
或application.yml
中配置Undertow的相关参数,例如端口、线程池大小等。
application.yml示例:
server:
port: 8080
undertow:
buffer-size: 1024
worker-threads: 8
direct-buffers: true
application.properties
示例:
server.port=8080
server.undertow.buffer-size=1024
server.undertow.direct-buffers=true
server.undertow.worker-threads: 8
编写反应式控制器
mport org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class DemoController {
@GetMapping("/demo")
public Flux<String> demo() {
return Flux.just("demo", "from", "Spring", "WebFlux", "with", "Undertow");
}
}
运行这个应用程序时,Spring Boot 将自动使用 Undertow 作为服务器容器,并在配置的端口(默认是 8080)上启动服务。你可以通过访问 http://localhost:8080/demo
来测试控制器。
【注意】如果你想要使用 Undertow 的非阻塞特性,你需要确保你的应用程序代码也支持非阻塞 I/O,例如使用 Spring WebFlux 或 Servlet 3.1 的异步支持。如果你使用的是传统的 Spring MVC,那么 Undertow 的非阻塞特性可能不会被充分利用。
UndertowServletWebServerFactory
配置类的细节:官方文档
Undertow
配置属性
配置项 | 说明 | 示例 |
server.undertow.accesslog.dir | Undertow 访问日志目录。 | |
server.undertow.accesslog.enabled | 是否启用访问日志。 | FALSE |
server.undertow.accesslog.pattern | 访问日志的格式。 | common |
server.undertow.accesslog.prefix | 日志文件前缀。 | access_log. |
server.undertow.accesslog.rotate | 是否开启日志滚动。 | TRUE |
server.undertow.accesslog.suffix | 日志文件后缀。 | log |
server.undertow.always-set-keep-alive | 是否应在所有响应中添加 Connection: keep-alive Header,即使 HTTP 规范没有要求。 | TRUE |
server.undertow.buffer-size | 每个 buffer 的大小。默认大小是根据 JVM 可用的最大内存确定的。 | |
server.undertow.decode-slash | 是否应解码已编码的斜线字符(%2F)。如果前端代理不执行相同的解码,解码可能会导致安全问题。只有在传统应用程序需要时才启用。设置后,server.undertow.allow-encoded-slash 无效。 | |
server.undertow.decode-url | 是否对 URL 进行解码。禁用时,URL 中的百分比编码字符将保持原样。 | TRUE |
server.undertow.direct-buffers | 是否在 Java 堆外分配 buffer。默认大小是根据 JVM 可用的最大内存确定的。 | |
server.undertow.eager-filter-init | 是否应在启动时初始化 servlet Filter | TRUE |
server.undertow.max-cookies | 允许的最大 cookie 数量。这一限制是为了防止基于哈希碰撞的 DOS 攻击。 | 200 |
server.undertow.max-headers | 允许的最大 header 数量。这一限制是为了防止基于哈希碰撞的 DOS 攻击。 | |
server.undertow.max-http-post-size | HTTP post content 的最大大小。当值为-1(默认值)时,大小为无限。 | -1B |
server.undertow.max-parameters | 允许查询或路径参数的最大数量。这一限制是为了防止基于哈希碰撞的 DOS 攻击。 | |
server.undertow.no-request-timeout | 在服务器关闭连接之前,连接在不处理请求的情况下闲置的时间。 | |
server.undertow.options.server.* | 在 io.undertow.UndertowOptions 中定义的服务器选项。 | |
server.undertow.options.socket.* | 在 org.xnio.Options 中定义的 socket 选项。 | |
server.undertow.preserve-path-on-forward | 转发请求时是否保留请求路径。 | FALSE |
server.undertow.threads.io | I/O 线程数。默认值为可用的处理器数量。 | |
server.undertow.threads.worker | Worker 线程数。默认为 I/O 线程数的 8 倍。 | |
server.undertow.url-charset | 用于解码 URL 的字符集。 | UTF-8 |