目录
基础知识
在了解学习webflux之前,需要学习和掌握一些基本知识,主要包括响应式编程和lambda相关。
lambda
取函数式编程之所长。
|
为什么?奥妙在哪?
java8开始存在,定义了FunctionalInterface接口。这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。
以lambda作为参数的方法能够推断出来lambda所表示的是哪个函数式接口的那个抽象方法。
在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。
stream
Stream(流)是一个来自数据源的元素队列并支持聚合操作
分为 中间操作 和 最终操作,在最终操作没有调用的情况下,所有的中间操作都不会执行。
Stream流有一些特性:
- Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。
- 这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。
- Stream不保存数据,故每个Stream流只能使用一次。
响应式编程
1. 事件驱动
在一个 事件驱动 的应用程序中,组件之间的交互是通过松耦合的 生产者 (production)
和 消费者 (consumption)
来实现的。这些事件是以 异步 和 非阻塞 的方式发送和接收的。
事件驱动 的系统依靠 推模式 而不是 拉模式 或 投票表决,即 生产者 是在有消息时才推送数据给 消费者,而不是通过一种浪费资源方式:让 消费者 不断地 轮询 或 等待数据。
2. 实时响应
程序发起执行以后,应该 快速 返回存储 结果的上下文,把具体执行交给 后台线程。待处理完成以后,异步地将 真实返回值 封装在此 上下文 中,而不是 阻塞 程序的执行。实时响应是通过 异步 编程实现的,例如:发起调用后,快速返回类似 java8
中 CompletableFuture
对象。
3. 弹性机制
事件驱动的 松散耦合 提供了组件在失败下,可以抓获 完全隔离 的上下文场景,作为 消息封装,发送到下游组件。在具体编程时可以 检查错误 ,比如:是否接收到,接收的命令是否可执行等,并决定如何应对。
响应式流规范(Reactive Streams)
在 Java
平台上,Netflix
(开发了 RxJava
)、TypeSafe
(开发了 Scala
、Akka
)、Pivatol
(开发了 Spring
、Reactor
)共同制定了一个被称为 Reactive Streams
项目(规范),用于制定反应式编程相关的规范以及接口。java社区的响应式流规范(响应式流规范)
Reactive Streams
由以下几个组件组成:
- 发布者:发布元素到订阅者
- 订阅者:消费元素
- 订阅:在发布者中,订阅被创建时,将与订阅者共享
- 处理器:发布者与订阅者之间处理数据
其主要的接口有这三个:
- Publisher
- Subscriber
- Subcription
1.Publisher是能够发出元素的发布者。
|
这里强调一下 Publisher
接口中的 subscribe
方法语义上有些奇特,它表示的不是订阅关系,而是被订阅关系。即 aPublisher.subscribe(aSubscriber)
表示的是 aPublisher
被 aSubscriber
订阅。
2.Subscriber是接收元素并做出响应的订阅者。
|
当执行subscribe方法时,发布者会回调订阅者的onSubscribe方法,这个方法中,通常订阅者会借助传入的Subscription向发布者请求n个数据。然后发布者通过不断调用订阅者的onNext方法向订阅者发出最多n个数据。如果数据全部发完,则会调用onComplete告知订阅者流已经发完;如果有错误发生,则通过onError发出错误数据,同样也会终止流。
订阅后的回调用表达式表示就是onSubscribe onNext* (onError | onComplete)?,即以一个onSubscribe开始,中间有0个或多个onNext,最后有0个或1个onError或onComplete事件。
Publisher和Subscriber融合了迭代器模式和观察者模式。
我们经常用到的Iterable和Iterator就是迭代器模式的体现,可以满足上边第1和2个特点关于按需处理数据流的要求;而观察者模式基于事件的回调机制有助于满足第3个特点关于异步传递元素的要求。
3.Subscription
是Publisher
和Subscriber
的“中间人”。
|
4.Processor
集Publisher
和Subscriber
于一身。
|
reactive stream(响应式流)
定义
具备“异步非阻塞”特性和“流量控制”能力的数据流,我们称之为响应式流(Reactive Stream)。
异步非阻塞
cpu很快,io很慢,所以需要非阻塞
异步—多线程(但是多线程也有线程切换和线程资源的问题)
非阻塞—回调和CompletableFuture(代码太难写)
流量控制
发布者发出数据的速度和订阅者处理数据的速度不同的时候,怎么办呢?
向上游反馈流量请求的机制就叫做回压(backpressure)
回压有不同策略:缓存策略和丢弃策略
基于响应式流的“升级版”的响应式编程
Reactor3和RxJava2都是具有以上特点的响应式流的具体实现库。
Reactor
这是一个基于 Java 8 的实现了响应式流规范 (Reactive Streams specification)的响应式库。
Flux与Mono
Reactor中的发布者(Publisher)由Flux
和Mono
两个类定义,它们都提供了丰富的操作符(operator)。一个Flux对象代表一个包含0..N个元素的响应式序列,而一个Mono对象代表一个包含零/一个(0..1)元素的结果。
核心调用过程
Reactor 的核心调用过程大致可以分为图中的几个阶段
- 声明:无论是使用 just 或者其他什么方式创建反应式流,这个过程都可以称之为声明,因为此时这些代码不会被实际的执行。
- subscribe:当调用 subscribe 时,整个执行过程便进入 subscribe 阶段,经过一系列的调用之后,subscribe 动作会代理给具体的 Flux 来实现。
- onSubscribe:onSubscribe 阶段指的是 Subscriber#onSubscribe 方法被依次调用的阶段。这个阶段会让各 Subscriber 知道 subscribe 方法已被触发,真正的处理流程马上就要开始。
- request:onSubscribe 阶段是表示订阅动作的方式,让各 Subscriber 知悉,准备开始处理数据。当最终的 Subscriber 做好处理数据的准备之后,它便会调用 - Subscription 的 request 方法请求数据。
- onNext:通过调用 Subscriber 的 onNext 方法,进行真正的响应式的数据处理。
- onComplete:成功的终端状态,没有进一步的事件将被发送。
- onError:错误的终端状态(和 onComplete 一样,当发生时,后面的将不会在继续执行)。
反应式编程和传统风格的编程一个最大的不同点在于,开发人员编写的大部分代码都是在声明处理过程。即这些代码并不会被实际的执行,直到开始被订阅。这便是为何第一阶段是声明阶段的原因。
如何实现?看下demo吧
Servlet3.0
Servlet 3.0 新增了请求的异步处理,使用异步servlet主要原因就是因为,在service方法中业务逻辑如果碰到io操作时间比较长的操作,这样这个service方法就会长时间占用tomcat容器线程池中的线程,这样是不利于其他请求的处理的,当线程池中的线程处理任务时,任务由于长时间io操作,肯定会阻塞线程处理其他任务,引入异步servlet的目的就是将容器线程池和业务线程池分离开。在处理大io的业务操作的时候,把这个操作移动到业务线程池中进行,释放容器线程,使得容器线程处理其他任务,在业务逻辑执行完毕之后,然后在通知tomcat容器线程池来继续后面的操作,这个操作应该是把处理结果commit到客户端或者是dispatch到其他servlet上。
springmvc使用方式:
|
WebFlux
Spring WebFlux是随Spring 5推出的响应式Web框架。
服务端技术栈
Spring提供了完整的支持响应式的服务端技术栈。
如上图所示,左侧为基于spring-webmvc的技术栈,右侧为基于spring-webflux的技术栈,
- Spring WebFlux是基于响应式流的,因此可以用来建立异步的、非阻塞的、事件驱动的服务。它采用Reactor作为首选的响应式流的实现库,不过也提供了对RxJava的支持。
- 由于响应式编程的特性,Spring WebFlux和Reactor底层需要支持异步的运行环境,比如Netty和Undertow;也可以运行在支持异步I/O的Servlet 3.1的容器之上,比如Tomcat(8.0.23及以上)和Jetty(9.0.4及以上)。
- 从图的纵向上看,spring-webflux上层支持两种开发模式:
- 类似于Spring WebMVC的基于注解(@Controller、@RequestMapping)的开发模式;·
- Java 8 lambda 风格的函数式开发模式。
- Spring WebFlux也支持响应式的Websocket服务端开发。
例子:
|
响应式Http客户端
此外,Spring WebFlux也提供了一个响应式的Http客户端API WebClient。它可以用函数式的方式异步非阻塞地发起Http请求并处理响应。其底层也是由Netty提供的异步支持。
我们可以把WebClient看做是响应式的RestTemplate,与后者相比,前者:
- 是非阻塞的,可以基于少量的线程处理更高的并发;
- 可以使用Java 8 lambda表达式;
- 支持异步的同时也可以支持同步的使用方式;
- 可以通过数据流的方式与服务端进行双向通信。
当然,与服务端对应的,Spring WebFlux也提供了响应式的Websocket客户端API。
例子:
|
参考文档
- https://blog.csdn.net/get_set/article/details/79455258
- https://xwjie.github.io/webflux/webflux-study-path.html
- https://htmlpreview.github.io/?https://github.com/get-set/reactor-core/blob/master-zh/src/docs/index.html#core-features