Spring WebFlux 教程:如何构建一个简单的响应应式 Web 应用程序

什么是反应式系统(Reactive System)?

反应式系统是采用反应式架构模式设计的系统,该模式优先考虑使用松散耦合、灵活和可扩展的组件。它们在设计时还考虑了故障解决方案,以确保即使一个系统出现故障,大部分系统仍能运行。

反应式系统专注于:

  • Reactiveness:最重要的考虑因素,反应性系统应该快速响应任何用户输入。响应式系统的拥护者认为,响应式有助于优化系统的所有其他部分,从数据收集到用户体验。
  • Resilience:反应性系统的设计应能够预测系统故障。反应式系统期望组件最终会失败,并设计松散耦合的系统,即使几个单独的部分停止工作也可以保持活动状态。
  • Elasticity:反应式系统应通过向上或向下扩展以满足需求来适应工作负载的大小。许多反应式系统还将使用预测缩放来预测和准备突然的转变。实现弹性的关键是消除任何瓶颈并构建可以根据需要分片或复制组件的系统。
  • 消息驱动的通信(Message-driven communication):反应式系统的所有组件都是松散耦合的,每个组件之间都有硬边界。您的系统应该通过显式消息传递跨越这些边界进行通信。这些消息让不同的组件了解失败情况,并帮助它们将工作流委托给可以处理它的组件。

反应式和其他网络模式之间最显着的区别是反应式系统可以一次执行多个未阻塞的调用,而不是让一些调用等待其他调用。因此,响应式系统可以提高性能和响应速度,因为 Web 应用程序的每个部分都可以比等待另一部分更快地完成自己的工作。

简而言之,反应式系统使用松散耦合、畅通无阻的组件来提高性能、用户体验和错误处理。

什么是Project Reactor?

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?

Spring WebFlux 是一个基于 Project Reactor 的完全非阻塞、基于注解的 Web 框架,可以在 HTTP 层上构建反应式应用程序。WebFlux 使用新的路由器函数功能将函数式编程应用于 Web 层并绕过声明式控制器和 RequestMappings。WebFlux 要求您将 Reactor 作为核心依赖项导入。

WebFlux 是在 Spring 5 中添加的,作为[Spring MVC 的] 反应式替代品,增加了对以下内容的支持:

  • 非阻塞线程:完成指定任务而无需等待先前任务完成的并发线程。
  • Reactive Stream API:一种标准化工具,包括用于使用非阻塞背压进行异步流处理的选项。
  • 异步数据处理:当数据在后台处理时,用户可以不间断地继续使用正常的应用程序功能。

最终,WebFlux 取消了 SpringMVC 的线程请求模型,而是使用多事件循环非阻塞模型来启用反应性、可扩展的应用程序。凭借对 Netty、Undertow 和 Servlet 3.1+ 容器等流行服务器的支持,WebFlux 已成为反应式堆栈的关键部分。

Spring 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,因此可以在任何配置类中创建。

路由器功能避免了请求映射的多步骤过程引起的潜在副作用,而是将其简化为直接的路由器/处理程序链。这允许响应式编程的函数式编程实现。

RequestMappingController注释样式在 WebFlux 中仍然有效,如果您更喜欢旧样式,RouterFunctions这只是您解决方案的一个新选项。

Web客户端

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 是一个导入的函数集合,允许更智能的流数据流。它内置了对背压和异步处理的支持,确保应用程序最有效地利用计算机和组件资源。

Reactive Stream API 中主要有四个接口:

  • Publisher``Subscribers:根据他们的需求将事件发送到链接。充当subscribers可以监视事件的中央链接点。

  • Subscriber:接收和处理由 发出的事件Publisher。多个Subscribers可以链接到单个Publisher并对同一事件做出不同的响应。订户可以设置为做出反应:

    • onNext,当它收到下一个事件时。
    • onSubscribe,当添加新订阅者时
    • onError,当另一个订阅者发生错误时
    • onComplete, 当另一个订阅者完成它的任务时
  • SubscriptionPublisher:定义 selected和之间的关系Subscriber。每个Subscriber只能链接到一个Publisher.

  • Processor : 代表处理阶段Subscriber

Servers

WebFlux 在 Tomcat、Jetty、Servlet 3.1+ 容器以及非 Servlet 运行时(如 Netty 和 Undertow)上受支持。Netty 最常用于异步和非阻塞设计,因此 WebFlux 将默认使用它。您只需简单更改 Maven 或 Gradle 构建软件,即可在这些服务器选项之间轻松切换。

这使得 WebFlux 在它可以使用的技术方面具有高度的通用性,并允许您使用现有的基础设施轻松实现它。

并发模型

WebFlux 在构建时考虑到了非阻塞,因此使用了与 Spring MVC 不同的并发编程模型。

Spring MVC 假定线程将被阻塞,并使用大型线程池在阻塞实例期间保持移动。这个更大的线程池使 MVC 占用更多资源,因为计算机硬件必须同时启动更多线程。

WebFlux 而是使用一个小线程池,因为它假设您永远不需要通过工作来避免阻塞。这些线程称为事件循环工作者,数量固定,并且比 MVC 线程更快地循环传入请求。这意味着 WebFlux 可以更有效地使用计算机资源,因为活动线程始终在工作。

Spring 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标签,这些标签允许他们具有一定级别的访问权限。

开始使用 Spring WebFlux

现在让我们开始使用 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 应用程序的最低要求。

路由Router

首先,我们将创建一个示例路由以在 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); } }

处理程序Handler

现在我们将添加一个处理程序来侦听任何请求路由的用户/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!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

youtian.L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值