WebFlux响应式框架原理介绍
WebFlux简介
WebFlux是什么?
一个响应式的MVC框架。
官网地址:https://docs.spring.io/spring-framework/docs/6.0.6/reference/html/web-reactive.html#webflux
什么是响应式程序
响应式处理是一种范式,使开发人员能够构建能够处理背压(流量控制)的非阻塞异步应用程序。 详见官网:https://spring.io/reactive
为什么使用响应式程序
响应式系统更好地利用现代处理器。此外,在反应式编程中包含背压可确保解耦组件之间具有更好的弹性。
SpringBoot 中的响应式架构图
响应式系统具有某些特性,使其成为低延迟、高吞吐量工作负载的理想选择。Project Reactor和Spring产品组合协同工作,使开发人员能够构建响应性、弹性、弹性和消息驱动的企业级反应系统。
开发使用 Spring MVC 还是 WebFlux?
这是一个很自然的问题,但却建立了一种不合理的二分法。事实上,两者共同努力扩大了可用选项的范围。两者的设计是为了彼此的连续性和一致性,它们可以并排使用,双方的反馈对双方都有利。下图显示了两者之间的关系、它们的共同点以及各自唯一支持的内容:
特点
响应式宣言(The Reactive Manifesto)
- 快速响应(Responsive)
系统在各种情况下都会尽全力保证及时响应。它是可用性和实用性的基础, 还意味着问题能被迅速发现,并得到有效处理。 - 回弹性(Resilient)
系统在面临故障时依然保持快速响应。它是通过复制(replication)、抑制(containment)、隔离(isolation)和委托(delegation)来实现的。故障被抑制在单个组件中, 且各组件相互隔离, 使系统在部分失败和恢复时,可以不影响整体的功能。每个组件的恢复都委托给另一个(外部)组件, 高可用在必要时通过复制来保证。 - 可伸缩性(Elastic)
系统在不同的负载下都能保持快速的响应。响应式系统可以根据服务的请求量, 动态增加或减少相应的资源。 - 消息驱动(Message Driven)
响应式系统依赖异步的消息传递, 以确定各种组件的边界, 并确保松耦合(loose coupling)、隔离性(isolation)、位置透明性(location transparency), 并提供将错误封装为消息的手段。
WebFlux的特点
-
异步和非阻塞
支持了应用层的异步和底层的IO非阻塞。在应用层,利用Reactive Stream定义的异步组件,实现了异步调用。在底层,默认使用了Netty的NIO,实现了(同步)非阻塞。这不会使程序运行更快,但是可以用少量的线程承受住高负载,节省线程内存资源和减少线程上下文切换CPU耗时,进而提升吞吐量。 -
函数式编程
使用Lambda表达式和函数式接口来定义请求处理程序。WebFlux.Fn是一种轻量级函数式编程模型,其中函数用于路由和处理请求,契约设计为不可变。它是基于注释的编程模型的另一种选择,但在其他方面运行在相同的Reactive Core基础上。例如,RouterFunction相当于@RequestMapping注释。 -
去servlet
允许可以不基于servlet API。
默认的Netty容器,不基于Servlet API。Servlet3.1支持了异步、非阻塞通信,因此,也可以选择使用Tomcat等容器,走Servlet API,但是,必须要使用WebFlux的框架代码。
代码实现
maven配置
<!-- WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Controller 接口
import com.demo.service.DemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
@Slf4j
@RestController
@RequestMapping("/flux")
public class DemoController {
@Resource
private DemoService demoService;
/**
* webflux接口测试(返回 0个 或 1个结果)
*/
@GetMapping("/monoTest")
public Mono<Object> monoTest() {
/// 写法一:命令式写法
// String data = getOneResult("monoTest()");
// return Mono.just(data);
// 写法二:响应式写法(语句需要在流中执行)
return Mono.create(cityMonoSink -> {
String data = demoService.getOneResult("monoTest()");
cityMonoSink.success(data);
});
}
/**
* webflux接口测试(返回 0个 或 多个结果)
*/
@GetMapping("/fluxTest")
public Flux<Object> fluxTest() {
// 写法一:命令式写法
// List<String> list = getMultiResult("fluxTest()");
// return Flux.fromIterable(list);
// 写法二:响应式写法(语句需要在流中执行)
return Flux.fromIterable(demoService.getMultiResult("fluxTest()"));
}
}
理论支撑
阿姆达尔定律
系统中对某一部件采用更快执行方式所能获得的系统性能改进程度,取决于这种执行方式被使用的频率,或所占总执行时间的比例。它指出了并行可以提升加速比,依此多核多线程可以提速。也指出了加速比是由慢的(串行的)系统部件所限制。
通用可伸缩性定律
阿姆达尔定律解释了可伸缩性,但是实际应用程序并不完全符合此定律,当并发线程比CPU核数多得多时,吞吐量反而会下降。因此,在通用可伸缩性定律(简称USL)中,引入了CONTENTION (竞争)和COHERENCY(一致性):
- CONTENTION 表示由于共享资源的等待或排队。
- COHERENCY 表示由于分布式资源之间的点对点数据交换导致数据变得一致或缓存一致的延迟。
原理
观察者模式
观察者模式是经典的设计模式之一。被观察者发生改变时,通知所有的观察者,从而解耦被观察者和观察者。在WebFlux的Reactive Stream的实现中,使用了观察者模式。
背压
背压(Backpressure)是WebFlux中用于解决生产者和消费者之间的速度不匹配问题的一种机制。当生产者的速度快于消费者的处理速度时,背压机制允许消费者告知生产者自己的处理能力,从而使生产者根据消费者的处理能力来调整数据的生产速度,以避免数据的积压和丢失。背压的实现主要依赖于Reactor库中的Flux和Mono两个类,这两个类都提供了一系列的操作符,用于处理数据流,并且支持背压机制。
WebFlux的初始化过程中,会去Spring上下文中找名为“webHandler”的WebHandler实现。默认情况下,Spring会在上下文中初始化一个DispatcherHandler的实现,Bean的name就是“webHandler”。这个里面维护了一个HandlerMapping列表,当请求过来时会迭代HandlerMapping列表,返回一个WebHandler处理。
WebFlux支持控制器和路由器模式的编码,因为他们分别有实现的HandlerMapping,能够在WebHandler的handler里路由到具体的业务方法里。通过@Controller和@ResultMaping定义的接口信息,WebFlux实现了在Spring WebFlux里的灵魂一样的存在。
总结
官网:WebFlux并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性。
实际,高并发下,BIO高并发的响应时间会变长,而WebFlux的平均响应时间相对稳定,效果如同缩短了响应时间。
- 适用于IO密集型场景,主要是指网络IO密集
例如:微服务网关,网关用http进行服务转发,适合使用webClient实现非阻塞调用。 - 不适用于CPU密集型场景
注:部分图片引用自网络,侵删。
标签:异步,架构,WebFlux,背压,观察者,响应,import From: https://blog.csdn.net/qq_36083245/article/details/139103200