WebFlux 学习(1)响应式编程简介

在传统的命令式编程模式,程序都是按照人工编写的指令(一般情况下,不考虑重排序)一步一步执行下去,下一步的执行需要等待之前的命令完成,也就是说,这条线程一直是在阻塞、执行中交替进行。无论怎么进行优化代码,提升性能,但是本质上还是需要依赖上一个任务的完成。

那么,什么是响应式编程呢?

这里引用 wikipedia 上的解释,比较抽象,所以我决定一边使用,一边学习,并一边分享给大家。

响应式编程是就是对于数据流和传播改变的一种声明式的编程规范。这意味着可以通过编程语言轻松地表达静态(例如数组)或动态(例如事件发射器)数据流,并且存在相关执行模型内的推断依赖性,这有助于自动传播 数据流涉及的变化。

我们来看下wikipedia 给出的demo

在命令式编程的模式下,定义了 a=b+c ,那么 a 的值再执行了这条语句后就被分配了 b+c 的结果,不管后续 b、c 怎么发生变化都不会影响到 a 的值。但是在响应式编程中,当b、c发生变化后,a的值会自动进行更新,而不需要重新执行赋值语句。

为什么需要响应式编程?

1.任何同步/阻塞都是有害的,因为线程不能做任何事情,只能阻塞着,等待处理完毕。

当然现在很多系统都大量并发很多用户。通常是使用阻塞型代码和采取并行来处理的(采用更多的线程以及更多的硬件资源)。直到达到瓶颈的时候,就需要引入更多的线程,运行类似的阻塞代码,但是这种拓展方式对资源很容易产生并发、争用问题。

而且阻塞会浪费资源,比如等待网络连接(数据库请求,其他服务请求),就会导致线程处于空闲状态。但是为了获取硬件的全部资源,这点也是必要的。
undefined

2.异步 & 阻塞 也是有害的,首先会新建很多线程来异步处理,这里就会消费很多资源。

undefined

3.采取传统的异步非阻塞。为了解决上个问题的资源浪费,Java 中可以采用 callback 模式,或者使用 Future 对象来处理。但是这些并不好用,比如采用 callback 模式,当逻辑处理的不好,很容易形成 callback hell(即回调一层又一层的嵌套),导致阅读和维护都比较困难。

反应堆 Project Reactor

Project Reactor 也就是 Spring Flux 中的默认反应式堆的实现,在这里进行着重介绍(写文章时候版本为 3.1.6.RELEASE)。

在 Project Reactor 有这样一段解释什么是反应式堆。

Reactor是JVM的完全非阻塞响应式编程基础,具有高效的需求管理(以管理“背压”的形式)。它直接集成了Java 8功能的API,特别是CompletableFuture,Stream和 Duration。它提供了可组合的异步序列API Flux(用于[0…N]元素)和 Mono(用于[0 | 1]元素),广泛地实现了Reactive Extensions规范。

maven 搭建

单纯的 maven 依赖如下

<dependencies>
    <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
            <version>3.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
            <version>3.1.6.RELEASE</version>
        </dependency>
</dependencies>

或者可以直接添加 Spring WebFlux 模块,地址如下
https://github.com/radium4ye/webflux_demo/blob/master/chapter0/pom.xml

反应式堆的特性

  • 可组合性&可读性
  • 直到订阅才会发生任何事情
  • 采用背压或通过其他方式消费者向生产者发出排放率过高的信号的能力
  • 具体丰富的数据流运算符
  • 高水平但高价值的抽象是并发性不可知的
可组合性

可组合性,表示我们可以编排很多任务,使用之前的任务的结果,作为之后任务的起始数据。

可读性

在传统任务编排模式下,随着异步的层数和复杂度的增加,导致代码变得越来越难读以及越来越难维护。比如说的 Callback Hell,就是在回调函数中添加另外一个回调。这样的代码,程序员很难预测数据走向,以至于很难排查问题。

Reactor提供了丰富的组合选项,其中代码反映了抽象过程的组织结构,并且所有内容通常保持在同一级别(嵌套最小化)。

装配线比喻

我们可以将反应式应用程序处理的数据视为在装配线上移动。反应器既是传送带又是工作站。原材料从Publisher中倒出,最终成为准备推送给消费者(或Subscriber)的成品。

原材料可以经历各种转化和其他中间步骤,或成为较大装配线的一部分,作为中间件。如果在某一点出现故障或堵塞(也许装箱产品花费的时间过长),受影响的工作站可向上游发出信号以限制原材料的流动,在反应式堆中成为背压。

没有事会发生,直到调用subscribe()

在反应堆中,当构造一个 Publisher 数据操作链的时候,数据在默认情况下并不会输入。直到调用subscribe(),将 Subscriber 绑定到 Publisher 上,才会触发整个数据链,并且 Subscriber 会通过 request() 方法向上传播,对数据的需求程度,这个信号会一直到传递到 Publisher。

背压 Backpressure

背压是一种流速控制的策略。在异步场景中,生成者发送事件速度远快于订阅者(或者是中间数据处理者)的处理速度的情况下,一种告诉上游的生成者降低发送速度的策略。

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值