Spring Boot 2系列(六十二):集成 WebFux 实现响应式Web

Spring Framework 5.0 版本增加了响应式 Web 框架 Spring WebFlux 。 它是完全无阻塞的,支持 Reactive Streams 反背压,并且可以在 Netty、Undertow 和 Servlet 3.1+ 容器等服务器上运行。

Spring Boot 通过为 Spring Webflux 提供自动配置来简化响应式 Web 应用程序的开发。

Reactive

Reactive:响应式,指围绕响应变化而构建的编程模型 — 网络组件响应 I/O 事件、UI 控制器响应鼠标事件等。

从这个意义上说,非阻塞是响应式的,因为不是被阻塞,而是在操作完成或数据可用时对通知做出反应。

我们在 Spring 团队中还有另一个与“反应式”相关的重要机制,那就是非阻塞背压。在同步的命令式代码中,阻塞调用是一种自然的背压形式,迫使调用者等待。在非阻塞代码中,控制事件的速率变得很重要,这样快速的生产者就不会压倒它的目的地。

Reactive Streams 是一个小规范(Java 9 也采用),它定义了具有背压的异步组件之间的交互。例如,数据存储库(充当发布者)可以生成 HTTP 服务器(充当订阅者)然后可以写入响应的数据。 Reactive Streams 的主要目的是让订阅者控制发布者生成数据的速度或速度。

Spring WebFlux

Spring Boot 通过为 Spring Webflux 提供自动配置来简化响应式 Web 应用程序的开发。

Spring WebFlux 是 Spring Framework 5.0 中引入的新的响应式 Web 框架。 与 Spring MVC 不同,它不需要 Servlet API,完全异步和非阻塞,并通过 Reactor 项目实现 Reactive Streams 规范。

Spring WebFlux 有两种风格:函数式和基于注释的。 基于注解的模型非常接近 Spring MVC 模型,如下例所示:

注解风格

基于注解的模型非常接近 Spring MVC 模型,如下例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RestController
@RequestMapping("/users")
public class MyRestController {

private final UserRepository userRepository;

private final CustomerRepository customerRepository;

public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) {
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}

@GetMapping("/{userId}")
public Mono<User> getUser(@PathVariable Long userId) {
return this.userRepository.findById(userId);
}

@GetMapping("/{userId}/customers")
public Flux<Customer> getUserCustomers(@PathVariable Long userId) {
return this.userRepository.findById(userId).flatMapMany(this.customerRepository::findByUser);
}

@DeleteMapping("/{userId}")
public Mono<Void> deleteUser(@PathVariable Long userId) {
return this.userRepository.deleteById(userId);
}

}

函数式风格

WebFlux.fn 函数式风格,将路由配置与请求的实际处理分开,如下例所示:

路由配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {

private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);

@Bean
public RouterFunction<ServerResponse> monoRouterFunction(MyUserHandler userHandler) {
return route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build();
}

}

业务处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class MyUserHandler {

public Mono<ServerResponse> getUser(ServerRequest request) {
...
}

public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
...
}

public Mono<ServerResponse> deleteUser(ServerRequest request) {
...
}

}

提示: 可以定义任意数量的 RouterFunction Bean,以将路由器按模块定义。 如果需要设置优先级,可以使用 @Order 注解 Bean 进行排序。

备注:如果在应用程序中同时添加 spring-boot-starter-web 和 spring-boot-starter-webflux 模块,会导致 Spring Boot 自动配置 Spring MVC,而不是 WebFlux。

之所以选择这种行为,是因为许多 Spring 开发人员将 spring-boot-starter-webflux 添加到他们的 Spring MVC 应用程序以使用响应式 WebClient。

您仍然可以通过将所选应用程序类型设置为 SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) 来强制执行您的选择。

自动配置

Spring Boot 为 Spring WebFlux 提供了自动配置,适用于大多数应用程序。

自动配置在Spring默认设置的基础上添加了以下功能:

如果想保留 Spring Boot WebFlux 的特性并且想添加额外的 WebFlux 配置,可以添加自己的 WebFluxConfigurer 类型的 @Configuration 类,但不需要 @EnableWebFlux注解。

如果想完全控制 Spring WebFlux,可以添加你自己的带有 @EnableWebFlux 注释的 @Configuration

消息转换器

带有HttpMessageReaderHttpMessageWriter的HTTP编解码器。

Spring WebFlux 使用 HttpMessageReader 和 HttpMessageWriter 接口转换 HTTP 请求和响应。通过查找类路径中可用的库,使用 CodeConfigurer 对它们进行配置,以获得合理的默认值。

Spring Boot 为编解码器 spring.codec.* 提供了专用的配置属性。 它还通过使用 CodecCustomizer 实例应用进一步的自定义。 例如,spring.jackson.* 配置键应用于 Jackson 编解码器。

如果需要添加或自定义编解码器,可以创建自定义 CodecCustomizer 组件,如下例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration(proxyBeanMethods = false)
public class MyCodecsConfiguration {

@Bean
public CodecCustomizer myCodecCustomizer() {
return (configurer) -> {
configurer.registerDefaults(false);
configurer.customCodecs().register(new ServerSentEventHttpMessageReader());
// ...
};
}

}

静态内容

默认情况下,Spring Boot 从类路径中名为 /static(或 /public 或 /resources 或 /META-INF/resources)的目录中提供静态内容。 它使用来自 Spring WebFlux 的 ResourceWebHandler,因此可以通过添加自己的 WebFluxConfigurer 并覆盖 addResourceHandlers 方法来修改该行为。

默认情况下,资源映射到 /**,但可以通过设置 spring.webflux.static-path-pattern 属性来调整它。 例如,将所有资源重定位到 /resources/** ,如下配置:

1
spring.webflux.static-path-pattern=/resources/**

还可以使用spring.web.resources.static-locations自定义静态资源位置。这样做将用目录位置列表替换默认值。

如果这样做,默认的欢迎页面检测将切换到自定义位置。所以,如果在启动时的任何位置有一个 index.html,它就是应用程序的主页(Home Page)。

除了前面列出的“标准”静态资源位置之外,还为 Webjars 内容做了一个特殊情况。 任何具有 /webjars/** 路径的资源如果以 Webjars 格式打包,则从 jar 文件中提供。

相关参考

  1. 官方:Reactive Web Applications

Spring Boot 2系列(六十二):集成 WebFux 实现响应式Web

http://blog.gxitsky.com/2022/07/22/SpringBoot-62-Flux-Web/

作者

光星

发布于

2022-07-22

更新于

2022-11-04

许可协议

评论