Spring5的webflux可以支持高吞吐量,使用相同的资源可以处理更加多的请求,它将会成为未来技术的趋势,但是相对于学习其他的框架相比,它的学习曲线很高,综合了很多现有的技术,即使按照教程学习能编写代码,但是仍然会感觉不够理解,要想真正的理解webflux,我觉得需要掌握以下几点:
1.学习jdk8的lambda表达式和stream流编程思想,
2.理解响应式编程概念,理解背压和实现机制。
理解了以上两点,很容易理解webflux的基石reactor,再学习webflux就很简单了!
学习lambda表达式
Lambda 表达式是一个匿名函数,源于数学λ演算。是闭包函数,但闭包并不一定是Lambda 函数。它可以赋值给变量,作为函数参数,作为函数返回值。
Lambda的本质在编译的时候在类中动态生成method方法,方法可以是static的也可以是非static的,主要取决于表达式内是否包含this这个变量,如果包含就生成动态的method方法,如果不包含就生成静态的method方法。
Lambda表达式的优点,大大的降低了编写代码的量,并大大提高了代码的可维护和可阅读性。
Lambda的惰性求值,只有在实际调用过程中才触发,像日志输出:
// 打印日志前需要先判断日志级别 if (logger.isLoggable(Level.DEBUG)) { logger.debug("====用户:" + username); }如果没有if条件判断,即使level不是debug,也会把执行“====用户:”+username的操作,浪费了性能,如果加上if条件判断代码量又很大,不利于代码的维护。如果用Lambda表达式是如何呢?
// 使用lambda表达式的惰性求值,不需要判断日志级别 logger.debug(()->"====用户:"+username);
因为Lambda的特点,实际上字符串相加的操作并不会执行,同时代码也少了,也容易维护了。
Stream流的概念
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。这个特点大大提高了硬件设备的利用率,提高了系统性能。
虽然大部分情况下stream是容器调用Collection.stream()方法得到的,但stream和collections有以下不同:
无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。对stream的操作分为为两类,中间操作(intermediate operations)和结束操作(terminal operations),二者特点是:
中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream,仅此而已。结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数。计算完成之后stream就会失效。响应式编程的概念
响应式编程是一种关注于数据流data streams)和事件传递的异步编程方式。它是面向对象编程中的“观察者模式”的在多线程异步编程一种实现。
在响应式流中, 当有新的数据到来的时候 ,由发布者(Publisher) 通知订阅者(Subscriber)。此外,对推送来的数据的操作 是通过一种声明式(declaratively)而不是命令式(imperatively)的方式表达的:开发者通过 描述“控制流程”来定义对数据流的处理逻辑。
除了数据推送以外,响应式编程对错误处理(error handling)和完成(completion)的定义实现也很完善。 一个 发布者 可以推送新的数据到它的 订阅者(调用 onNext 方法), 同样也可以推送错误(调用 onError 方法)和完成(调用 onComplete 方法)信号。 错误和完成信号都可以终止响应式流。
响应式编程的背压机制
背压是指在异步场景中,被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略。简而言之,背压是流速控制的一种策略。
理解了背压的概念,在整体设计的时候就要考虑流水线上的每一个操作的时候就知道该怎么设计,尽量每个环节的执行时间大约一致,保证整体的处理速度比较均衡。
reactor的概念
Project Reactor(以下简称“Reactor”)与Spring是兄弟项目,侧重于Server端的响应式编程,主要 artifact 是 reactor-core,这是一个基于 Java 8 的实现了响应式流规范 (Reactive Streams specification)的响应式库。
Reactor中的发布者(Publisher)由Flux和Mono两个类定义,它们都提供了丰富的操作符(operator)。一个Flux对象代表一个包含0..N个元素的响应式序列,而一个Mono对象代表一个包含零/一个(0..1)元素的结果。
既然是“数据流”的发布者,Flux和Mono都可以发出三种“数据信号”:元素值、错误信号、完成信号,错误信号和完成信号都是终止信号,完成信号用于告知下游订阅者该数据流正常结束,错误信号终止数据流的同时将错误传递给下游订阅者。
掌握了以上几点再学习webflux的时候,对webflux的基本原理就很清楚了,再学起来感觉会有豁然开朗的感觉!你觉得呢?
———— / END / ————