响应式系统提供了我们在高数据流世界中所需的无与伦比的响应能力和可扩展性。然而,反应式系统需要经过专门培训的工具和开发人员来实现这些独特的程序架构。带有ProjectReactor的SpringWebFlux是一个专为满足现代公司的响应式需求而构建的框架。今天,我们将通过解释WebFlux如何与其他反应式堆栈工具配合使用、它有何不同以及如何制作您的第一个应用程序来帮助您开始使用WebFlux。什么是反应系统?反应式系统是一种采用反应式架构模式设计的系统,该模式优先考虑使用松散耦合、灵活和可扩展的组件。它们在设计时还考虑了故障解决方案,以确保即使在发生故障时,系统的大部分仍能正常运行。反应式系统专注于:反应性:最重要的是,反应式系统应该快速响应任何用户输入。响应式系统的拥护者认为,响应式有助于优化系统的所有其他部分,从数据收集到用户体验。弹性:反应系统的设计应能够预测系统故障。反应式系统期望组件最终会失败,并设计松散耦合的系统,即使几个单独的部分停止工作也能保持活力。弹性:反应式系统应该通过向上或向下扩展以满足需求来适应工作负载的大小。许多反应式系统还将使用预测性扩展来预测和准备突然的变化。实现弹性的关键是消除任何瓶颈并构建可以根据需要分片或复制组件的系统。消息驱动的通信:反应式系统的所有组件都是松散耦合的,每个组件之间都有硬边界。您的系统应该通过显式消息传递跨越这些边界进行通信。这些消息让不同的组件知道失败并帮助它们将工作流委托给可以处理它的组件。反应式和其他网络模式之间最显着的区别是反应式系统可以一次执行多个非阻塞调用,而不是让一些调用等待其他调用。因此,反应式系统提高了性能和响应能力,因为Web应用程序的每个部分都可以比必须等待其他部分更快地完成其部分。什么是反应堆项目?ProjectReactor是由Pivotal构建并由Spring提供支持的框架。它实现了ReactiveAPI模式,最著名的是ReactiveStreams规范。如果您熟悉Java8Streams,您会很快发现Stream和Flux(或其单元素对应物Mono)之间的许多相似之处。它们之间的主要区别在于Fluxes和Monos遵循发布者-订阅者模式并实现背压,而StreamAPI则没有。背压是数据端点向数据生产者发出信号表明它已收到过多数据的一种方式。这允许更好的流量管理和分配,因为它可以防止单个组件过度工作。使用Reactor的主要优点是您可以完全控制数据流。您可以依靠订阅者在准备好处理信息时请求更多信息的能力,或者在发布者端缓冲一些结果,甚至使用无背压的全推送方法。在我们的反应堆栈中,它位于SpringBoot2.0和WebFlux之上:反应堆栈示例堆栈:技术堆栈是用于创建Web或移动应用程序的软件产品和编程语言的组合。反应堆是相同的,但用于创建反应性应用程序。什么是SpringWebFlux?SpringWebFlux是一个完全非阻塞的、基于注释的Web框架,构建在ProjectReactor之上,这使得在HTTP层之上构建反应式应用程序成为可能。WebFlux将函数式编程应用于Web层,并使用新的路由器功能特性绕过声明式控制器和请求映射。WebFlux要求您将Reactor作为核心依赖项导入。WebFlux是在Spring5中作为SpringMVC的响应式替代品添加的,并增加了对以下内容的支持:非阻塞线程:无需等待先前任务完成即可完成指定任务的并发线程。ReactiveStreamAPI:一种标准化设施,包括用于非阻塞背压的异步流处理选项。异步数据处理:当数据在后台处理时,用户可以继续使用正常的应用程序功能而不会中断。最终,WebFlux放弃了SpringMVC的多请求线程模型,转而使用多EventLoop非阻塞模型来实现反应式、可扩展的应用程序。凭借对Netty、Undertow和Servlet3.1+容器等流行服务器的支持,WebFlux已成为反应式堆栈的关键部分。路由器函数RouterFunction是标准springmvc中使用的@RequestMapping和@Controller注释样式的功能替代品。我们可以用它来将请求路由到处理函数:ProductServiceps){returnroute().GET("/product",req->ok().body(ps.findAll())).build();}您可以使用RouterFunctions.route()构建路由,而不是编写一个完整的路由器功能。路由被注册为springbean,因此它们可以在任何配置类中创建。路由器功能避免了请求映射的多步骤过程可能带来的副作用,而是将其简化为直接的路由器/处理程序链。这允许函数式编程实现反应式编程。RequestMapping和Controller注释样式在WebFlux中仍然有效如果您更熟悉旧样式,RouterFunctions只是解决方案的一个新选项。WebClient详解项目中经常用到发送Http请求的客户端。如果你使用webflux,创建一个Http请求是非常简单的。WebClient是基于著名的rest模板构建的WebFlux的反应式Web客户端。它是一个接口,表示Web请求的主要入口点,同时支持同步和异步操作。WebClient主要用于响应式后端到后端通信。您可以使用Maven导入标准WebFlux依赖项来构建和创建WebClient实例:org.springframework.bootspring-boot-starter-webflux创建实例WebClientwebClient=WebClient.create();//如果是调用具体服务的API,可以在初始化webclient时使用,baseUrlWebClientwebClient=WebClient.create("https://github.com/1ssqq1lxr");或者构造方法InitializeWebClientwebClient1=WebClient.builder().baseUrl("https://github.com/1ssqq1lxr").defaultHeader(HttpHeaders.CONTENT_TYPE,"application/vnd.github.v3+json").defaultHeader(HttpHeaders.USER_AGENT,"Spring5WebClient").build();获取请求Monoresp=WebClient.create().method(HttpMethod.GET).uri("https://github.com/1ssqq1lxr").cookie("token","xxxx").header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE).retrieve().bodyToMono(String.class);发布请求(表单)MultiValueMapformData=newLinkedMultiValueMap();formData.add("name1","value1");formData.add("name2","value2");Monoresp=WebClient.create().post().uri("http://www.w3school.com.cn/test/demo_form.asp").contentType(MediaType.APPLICATION_FORM_URLENCODED).body(BodyInserters.fromFormData(formData)).retrieve().bodyToMono(String.class);Post请求(Body)Bookbook=newBook();book.setName("name");book.setTitle("thisistitle");Monoresp=WebClient.create().post().uri("https://github.com/1ssqq1lxr").contentType(MediaType.APPLICATION_JSON_UTF8).body(Mono.just(book),Book.class).retrieve().bodyToMono(String.class);文件上传HttpHeadersheaders=newHttpHeaders();headers.setContentType(MediaType.IMAGE_PNG);HttpEntityentity=newHttpEntity<>(newClassPathResource("parallel.png"),headers);MultiValueMapparts=newLinkedMultiValueMap<>();arts.add("file",entity);Monoresp=WebClient.create().post().uri("http://localhost:8080/upload").contentType(MediaType.MULTIPART_FORM_DATA).body(BodyInserters.fromMultipartData(parts).retrieve().bodyToMono(String.class);ReactiveSteamAPI下篇文章将为大家详细介绍Reactor3的API,ReactiveStreamAPI是一组函数集合,可以让stream数据流更智能。支持用于压力和异步处理,确保应用程序最有效地使用计算机和组件资源。ReactiveStreamsAPI有四个主要接口:Publisher:按需向链接的订阅者发布事件。充当订阅者可以监视事件的中央链接点.Subscriber:接收并处理一个Publisher发出的事件。多个Subscriber可以链接到一个Publisher,对同一个事件做出不同的响应。一个Subscriber可以设置react:onNext,当它接收到下一个事件时。onSubscribe,onError时添加一个新的订阅,当另一个订阅者出错时onComplete,当订阅完成时服务器容器WebFlux在Tomcat、Jetty、servlet3.1+容器和非Servlet运行时(如Netty和Undertow)上受支持。Netty最常用于异步和非阻塞设计,因此WebFlux将默认使用它。通过对Maven或Gradle构建软件进行简单更改,可以轻松地在这些服务器选项之间切换。这使得WebFlux成为可以使用的地方。技术方面具有高度通用性,允许您使用现有的基础架构轻松实现它。并发模型WebFlux是在考虑非阻塞的情况下构建的,因此使用与springmvc不同的并发编程模型。springmvc假设线程会被阻塞,并使用一个大线程池在实例被阻塞时保持移动。这个更大的线程池使MVC更加占用资源,因为计算机硬件必须同时保留更多线程WebFlux使用一个小线程池,因为它假定您永远不需要传递工作以避免阻塞。有固定数量的这些线程,称为事件循环工作线程,它们比MVC线程更快地循环传入请求。这意味着WebFlux可以更有效地使用计算机资源,因为活动线程始终在工作。SpringWebFluxSecurityWebFlux使用SpringSecurity来实现身份验证和授权协议。SpringSecurity使用WebFilter根据经过身份验证的用户列表对请求进行身份验证。@EnableWebFluxSecuritypublicclassHelloWebFluxSecurityConfig{@BeanpublicMapReactiveUserDetailsS??erviceuserDetailsS??ervice(){UserDetailsuser=User.withDefaultPasswordEncoder().username("user").password("user").roles("USER").build();returnnewMapReactiveUserDetails;Service}(此处),我们可以看到用户有用户名、密码和一个或多个允许自定义访问的角色标签。类似于SpringBootSecurity的UserDetailsS??ervice接口,开始使用SpringWebFlux生成项目spring代码生成器参考配置。生成的pom如下4.0.0org.springframework.bootspring-boot-starter-parent2.5.1版本>com.github.webflux.learndemo0.0.1-SNAPSHOTdemoDemoprojectforSpringBoot1.8org.springframework.bootspring-boot-starter-webfluxorg.projectlomboklomboktrueorg.springframework.bootspring-boot-启动器-testtestio.projectreactorreactor-testtestorg.springframework.bootspring-boot-maven-plugin<配置><克oupId>org.projectlomboklombok开发接口自定义一个函数routing:获取请求路径中的占位参数作为返回值路径}").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),serverRequest->{Stringstr=serverRequest.pathVariable("path");returnServerResponse.ok().contentType(MediaType.TEXT_PLAIN).bodyValue(str).switchIfEmpty(ServerResponse.notFound().build());});}}浏览器请求http://localhost:4990/hello/hahahaha添加认证/***@authorcoding中*/@Configuration@EnableWebFluxSecuritypublicclassHelloWebfluxSecurityConfig{@BeanpublicMapReactiveUserDetailsS??erviceuserDetailsS??ervice(){UserDetailsuser=User.withDefaultPasswordEncoder().username("用户&曲ot;).password("user").roles("USER").build();returnnewMapReactiveUserDetailsS??ervice(user);}@BeanpublicSecurityWebFilterChainspringSecurityFilterChain(ServerHttpSecurityhttp){//@formatter:offreturnhttp.authorizeExchange().pathMatchers("/你好/**").authenticated().pathMatchers("/hello/login").permitAll().anyExchange().authenticated().and().formLogin().and().logout().and().httpBasic().and().csrf().disable().build();}}再次请求接口浏览器请求http://localhost:4990/hello/haha这时候浏览器被重定向了到http://localhost:4990/login登录页面输入用户/用户名和密码完成登录然后浏览器请求http://localhost:4990/hello/authenticateauthenticate