一、什么是WebFlux?
我们从Spring
的官网拉下一点点就可以看到介绍WebFlux
的地方了
从官网的简介中我们能得出什么样的信息?
- 我们程序员往往根据不同的应用场景选择不同的技术,有的场景适合用于同步阻塞的,有的场景适合用于异步非阻塞的。而
Spring5
提供了一整套响应式(非阻塞)的技术栈供我们使用(包括Web控制器、权限控制、数据访问层等等)。 - 而左侧的图则是技术栈的对比啦;
- 响应式一般用Netty或者Servlet 3.1的容器(因为支持异步非阻塞),而Servlet技术栈用的是Servlet容器
- 在Web端,响应式用的是WebFlux,Servlet用的是SpringMVC
- …..
总结起来,WebFlux只是响应式编程中的一部分(在Web控制端),所以一般我们用它与SpringMVC来对比。
二、如何理解响应式编程?
在上面提到了响应式编程(Reactive Programming),而WebFlux只是响应式编程的其中一个技术栈而已,所以我们先来探讨一下什么是响应式编程
从维基百科里边我们得到的定义:
reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change
响应式编程(reactive programming)是一种基于数据流(data stream)和变化传递(propagation of change)的声明式(declarative)的编程范式
在维基百科上也举了个小例子:
意思大概如下:
- 在命令式编程(我们的日常编程模式)下,式子
a=b+c
,这就意味着a
的值是由b
和c
计算出来的。如果b
或者c
后续有变化,不会影响到a
的值 - 在响应式编程下,式子
a:=b+c
,这就意味着a
的值是由b
和c
计算出来的。但如果b
或者c
的值后续有变化,会影响到a
的值
我认为上面的例子已经可以帮助我们理解变化传递(propagation of change)
那数据流(data stream)和声明式(declarative)怎么理解呢?那可以提一提我们的Stream流了。之前写过Lambda表达式和Stream流的文章,大家可以先去看看:
Lambda的语法是这样的(Stream流的使用会涉及到很多Lambda表达式的东西,所以一般先学Lambda再学Stream流):
Stream流的使用分为三个步骤(创建Stream流、执行中间操作、执行最终操作):
执行中间操作实际上就是给我们提供了很多的API去操作Stream流中的数据(求和/去重/过滤)等等
说了这么多,怎么理解数据流和声明式呢?其实是这样的:
- 本来数据是我们自行处理的,后来我们把要处理的数据抽象出来(变成了数据流),然后通过API去处理数据流中的数据(是声明式的)
比如下面的代码;将数组中的数据变成数据流,通过显式声明调用.sum()
来处理数据流中的数据,得到最终的结果:
public static void main(String[] args) { int[] nums = { 1, 2, 3 }; int sum2 = IntStream.of(nums).parallel().sum(); System.out.println("结果为:" + sum2); }
如图下所示:
2.1 响应式编程->异步非阻塞
上面讲了响应式编程是什么:
响应式编程(reactive programming)是一种基于数据流(data stream)和变化传递(propagation of change)的声明式(declarative)的编程范式
也讲解了数据流/变化传递/声明式是什么意思,但说到响应式编程就离不开异步非阻塞。
从Spring官网介绍WebFlux的信息我们就可以发现asynchronous, nonblocking
这样的字样,因为响应式编程它是异步的,也可以理解成变化传递它是异步执行的。
如下图,合计的金额会受其他的金额影响(更新的过程是异步的):
我们的JDK8 Stream流是同步的,它就不适合用于响应式编程(但基础的用法是需要懂的,因为响应式流编程都是操作流嘛)
而在JDK9 已经支持响应式流了,下面我们来看一下
三、JDK9 Reactive
响应式流的规范早已经被提出了:里面提到了:
Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure ----->http://www.reactive-streams.org/
翻译再加点信息:
响应式流(Reactive Streams)通过定义一组实体,接口和互操作方法,给出了实现异步非阻塞背压的标准。第三方遵循这个标准来实现具体的解决方案,常见的有Reactor,RxJava,Akka Streams,Ratpack等。
规范里头实际上就是定义了四个接口:
Java 平台直到 JDK 9才提供了对于Reactive的完整支持,JDK9也定义了上述提到的四个接口,在java.util.concurrent
包上
一个通用的流处理架构一般会是这样的(生产者产生数据,对数据进行中间处理,消费者拿到数据消费):
- 数据来源,一般称为生产者(Producer)
- 数据的目的地,一般称为消费者(Consumer)
- 在处理时,对数据执行某些操作一个或多个处理阶段。(Processor)
到这里我们再看回响应式流的接口,我们应该就能懂了:
- Publisher(发布者)相当于生产者(Producer)
- Subscriber(订阅者)相当于消费者(Consumer)
- Processor就是在发布者与订阅者之间处理数据用的
在响应式流上提到了back pressure(背压)这么一个概念,其实非常好理解。在响应式流实现异步非阻塞是基于生产者和消费者模式的,而生产者消费者很容易出现的一个问题就是:生产者生产数据多了,就把消费者给压垮了。