反应式系统是采用反应式架构模式设计的系统,该模式优先考虑使用松散耦合、灵活和可扩展的组件。它们在设计时还考虑了故障解决方案,以确保即使一个系统出现故障,大部分系统仍能运行。
反应式系统专注于:
反应式和其他网络模式之间最显着的区别是反应式系统可以一次执行多个未阻塞的调用,而不是让一些调用等待其他调用。因此,响应式系统可以提高性能和响应速度,因为 Web 应用程序的每个部分都可以比等待另一部分更快地完成自己的工作。
简而言之,反应式系统使用松散耦合、畅通无阻的组件来提高性能、用户体验和错误处理。
Project Reactor 是由 Pivotal 构建并由 Spring 提供支持的框架。它实现了响应式 API 模式,最著名的是 Reactive Streams 规范。
如果您熟悉Java 8 Streams,您会很快发现 Stream 和 Flux(或其单元素版本 Mono)之间有许多相似之处。它们之间的主要区别在于 Fluxes 和 Monos 遵循一种publisher-subscriber
模式并实现Backpressure,而 Stream API 则没有。
Backpressure是数据端点向数据生产者发出信号,表明它正在接收过多数据的一种方式。这允许更好的流量管理和分配,因为它可以防止单个组件过度工作。
使用 Reactor 的主要优点是您可以完全控制数据流。您可以依靠订阅者在准备好处理时请求更多信息的能力,或者在发布者端缓冲一些结果,甚至使用没有背压的全推送方法。
在我们的反应堆栈中,它位于 Spring Boot 2.0 之下和 WebFlux 之上:
堆栈: 技术堆栈是用于创建 Web 或移动应用程序的软件产品和编程语言的组合。反应式堆栈是相同的,但用于创建反应式应用程序。
Spring WebFlux 是一个基于 Project Reactor 的完全非阻塞、基于注解的 Web 框架,可以在 HTTP 层上构建反应式应用程序。WebFlux 使用新的路由器函数功能将函数式编程应用于 Web 层并绕过声明式控制器和 RequestMappings。WebFlux 要求您将 Reactor 作为核心依赖项导入。
WebFlux 是在 Spring 5 中添加的,作为[Spring MVC 的] 反应式替代品,增加了对以下内容的支持:
最终,WebFlux 取消了 SpringMVC 的线程请求模型,而是使用多事件循环非阻塞模型来启用反应性、可扩展的应用程序。凭借对 Netty、Undertow 和 Servlet 3.1+ 容器等流行服务器的支持,WebFlux 已成为反应式堆栈的关键部分。
RouterFunction``@RequestMapping
是标准 Spring MVC 中使用的和注释样式的功能替代@Controller
。
我们可以使用它来将请求路由到处理函数:
@RestController
public class ProductController {
@RequestMapping("/product")
public List<Product> productListing() {
return ps.findAll();
}
}
@Bean
public RouterFunction<ServerResponse> productListing(ProductService ps) {
return route().GET("/product", req -> ok().body(ps.findAll()))
.build();
}
您可以使用RouterFunctions.route()
创建路由而不是编写完整的路由器功能。路由被注册为 Spring bean,因此可以在任何配置类中创建。
路由器功能避免了请求映射的多步骤过程引起的潜在副作用,而是将其简化为直接的路由器/处理程序链。这允许响应式编程的函数式编程实现。
RequestMapping
和Controller
注释样式在 WebFlux 中仍然有效,如果您更喜欢旧样式,RouterFunctions
这只是您解决方案的一个新选项。
WebClient 是 WebFlux 的响应式 Web 客户端,由著名的RestTemplate
. 它是一个接口,表示 Web 请求的主要入口点,同时支持同步和异步操作。WebClient 主要用于响应式后端到后端通信。
您可以通过使用 Maven 导入标准 WebFlux 依赖项来构建和创建 WebClient 实例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
WebClient client = WebClient.create();
Reactive Stream API 是一个导入的函数集合,允许更智能的流数据流。它内置了对背压和异步处理的支持,确保应用程序最有效地利用计算机和组件资源。
Reactive Stream API 中主要有四个接口:
Publisher``Subscribers
:根据他们的需求将事件发送到链接。充当subscribers
可以监视事件的中央链接点。
Subscriber
:接收和处理由 发出的事件Publisher
。多个Subscribers
可以链接到单个Publisher
并对同一事件做出不同的响应。订户可以设置为做出反应:
onNext
,当它收到下一个事件时。onSubscribe
,当添加新订阅者时onError
,当另一个订阅者发生错误时onComplete
, 当另一个订阅者完成它的任务时Publisher
:定义 selected和之间的关系Subscriber
。每个Subscriber
只能链接到一个Publisher
.
Subscriber
WebFlux 在 Tomcat、Jetty、Servlet 3.1+ 容器以及非 Servlet 运行时(如 Netty 和 Undertow)上受支持。Netty 最常用于异步和非阻塞设计,因此 WebFlux 将默认使用它。您只需简单更改 Maven 或 Gradle 构建软件,即可在这些服务器选项之间轻松切换。
这使得 WebFlux 在它可以使用的技术方面具有高度的通用性,并允许您使用现有的基础设施轻松实现它。
WebFlux 在构建时考虑到了非阻塞,因此使用了与 Spring MVC 不同的并发编程模型。
Spring MVC 假定线程将被阻塞,并使用大型线程池在阻塞实例期间保持移动。这个更大的线程池使 MVC 占用更多资源,因为计算机硬件必须同时启动更多线程。
WebFlux 而是使用一个小线程池,因为它假设您永远不需要通过工作来避免阻塞。这些线程称为事件循环工作者,数量固定,并且比 MVC 线程更快地循环传入请求。这意味着 WebFlux 可以更有效地使用计算机资源,因为活动线程始终在工作。
WebFlux 使用 Spring Security 来实现[身份验证和授权协议]。Spring Security 用于WebFilter
根据经过身份验证的用户列表检查请求,或者可以将其设置为自动拒绝符合来源或请求类型等条件的请求。
@EnableWebFluxSecurity
public class HelloWebFluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
这是将所有设置设置为默认值的最小实现。在这里我们可以看到用户有 a username
、 apassword
和一个或多个roles
标签,这些标签允许他们具有一定级别的访问权限。
现在让我们开始使用 WebFlux。首先,我们需要建立一个项目。
我们将使用依赖Spring Initializr
项生成 Maven 构建Spring Reactive Web
。
这将生成一个pom.xml
如下所示的文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>reactive-rest-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>reactive-rest-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
从这里我们将添加一些组件来制作Hello-World
应用程序。我们将只添加一个路由器和一个处理程序,这是创建我们的基本 WebFlux 应用程序的最低要求。
首先,我们将创建一个示例路由以在 URL 处显示我们的文本一次http://localhost:8080/example
。这定义了用户如何请求我们将在处理程序中定义的数据。
@Configuration
public class ExampleRouter {
@Bean
public RouterFunction<ServerResponse> routeExample (ExampleHandler exampleHandler) {
return RouterFunctions
.route(RequestPredicates.GET("/example").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), exampleHandler::hello);
}
}
现在我们将添加一个处理程序来侦听任何请求路由的用户/example
。一旦路由器识别出请求的路径匹配,它会将用户发送给处理程序。我们的处理程序收到消息并将用户带到带有我们问候语的页面。
@Component
public class ExampleHandler {
public Mono<ServerResponse> hello(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("Hello, Spring WebFlux Example!"));
}
}
现在我们将通过执行 Maven 目标来运行我们的应用程序spring-boot:run
。您现在可以http://localhost:8080/example
在浏览器中访问以查找:
Hello, Spring WebFlux Example!