响应式编程

命令式编程(Imperative Programing),是一种描述计算机所需做出的行为的编程范式。详细的命令机器怎么(How)去处理以达到想要的结果(What)。

声明式编程(Declarative Programing),是一种编程范式,与命令式编程相对立。它描述目标的性质,让计算机明白目标,而非流程。只告诉机器想要的结果(What),机器自己摸索过程(How)。

响应式编程(Reactive Programing)是一种关注数据流(data streams)和变化传递(propagation of charge)的异步编程方式,它属于声明式编程范式。

上面的文字理论性比较强,说的直白一点:

数据流就像一条河:它可以被观测,被过滤,被操作(和 Java Stream 一致),或者为新的消费者与另外一条流合并为一条新的流。

响应式编程的一个关键概念是事件。事件可以被等待,可以触发过程,也可以除法其它事件。事件是唯一以合适的方式将我们的现实世界映射到我们的软件中:如果屋里太热了,我们就打开一扇窗。

同样的,当我们的天气 app 从服务器端获取到新的天气数据后,我们需要更新 app 上展示天气的 UI ;汽车上的车道偏移系统探测到车辆偏移了正常路线就会提醒驾驶者纠正;这些就是响应事件。

响应式编程可以理解为面向对象中观察者模式(Observer Design Pattern)的一种扩展,它也与迭代器模式(Iterator Design Pattern)

有相同之处,响应式流(reactive streams)其中也有 Iterable - Iterator 这样的对应关系,主要的区别在于 Iterator 是基于拉取(pull)

方式的,而响应式流是基于推送(push)方式的。

Iterator 迭代器(模式)也是命令式编程方式,即使访问值的方法使用的是 iterable 方法,什么时候获取 next 是由开发者决定的。在响应式流中,相对应的角色是发布者(publisher) - 订阅者(Subscriber),当有新值到来的时候,是由发布者通知订阅者,这种典型的推送方式是响应式流的关键,同时对推送来的数据的操作是声明式的表达,而不是命令式的,开发者通过描述控制流程来定义对数据流的处理逻辑。

除了数据推送,响应式流还提供了数据流完成的信号和发生错误的信号。一个发布者可以随时向订阅者推送数据(onNext),同时也可以推送错误(onError)和完成信号(onComplete),错误和完成信号都将终止响应流。

说了这么多理论性描述,那么响应式编程到底牛叉在哪里呢?

阻塞是资源浪费

以现实中的双11秒杀为例,当大量用户同时在0点发起抢购某个商品时,假设在不做任何限流、架构优化等的情况下,大量的请求将同时进入到后端,以tomcat容器为例,tomcat将为用户创建大量的线程(受线程池控制)来响应用户请求,后端的代码收到请求后,需要执行如下逻辑:判断用户的下单请求是否合理有效,判断用户是否参与过当前商品的秒杀,判断当前商品库存是否充足,如果库存充足,执行下单锁库存,随着并发数的加大,这一套代码逻辑执行下来将会花费很长时间,同时tomcat之前的线程一直阻塞着,等待servlet的结果来最终响应给用户。

现代应用面对大量的并发用户,即使硬件的处理能力高速发展,软件性能仍是关键因素。

广义来说,有两种方式来提高软件性能:

  1. 并行化,使用更多的线程和硬件资源;
  2. 优化现有(代码)资源,提高执行效率。

通常,开发者使用阻塞式编写代码,在出现性能瓶颈后,我们可以增加处理线程,线程中同样是阻塞的代码。但是这种使用资源的方式会面临资源的竞争和并发问题。同时,阻塞会浪费资源。具体来说,当一个程序面临延迟(通常是 I/O 方面,比如数据库读写和网络请求),所在线程进入 idle 状态等待返回,从而浪费资源。所以并行化并非解决问题的最佳方式,它是挖掘硬件潜力的方式,但是带来了复杂性,并造成了对资源的浪费。

异步可以解决问题嘛?

第 2 个思路,提高执行效率,通过编写异步非阻塞的代码,任务发起异步调用后,执行过程会切换到另一个(使用同样底层资源)活跃任务,等待异步任务返回结果后再去处理,这样可以解决资源的浪费问题。

Java 提供了2种方式实现异步代码:

  1. 回调:异步方法没有返回值,而是采用一个 callback 作为参数,当有结果后,回调这个 callback。常见的例子,比如 Swing 中

    的 EventListener。
  2. Future:异步方法立即返回一个 Future ,Future 的结果不是立马可以拿到,需要等待异步任务执行完成后,才可以使用。

但是这两个方法都有他们的局限性:

  1. 如果在回调中嵌套回调时,多层嵌套的回调将导致代码难以理解和维护,即所谓的嵌套地狱。
  2. Future 比回调要好一点,即使在 Java 8 中引入了 CompletableFuture,他对于多个处理的组合仍不友好。编排多个 Future 是可行的,

    但却不易。此外,Future 还有一个问题,当对 Future 对象调用 get() 方法时,仍然会导致阻塞,并且缺乏对多个值以及更进一步对错误

    的处理。

从命令式编程到响应式编程

基于上面提到的问题,开发牛人们提出了响应式流解决方案。在响应式编程方面,微软是先行者,他们率先在 .NET 中创建了响应式扩展库(Reactive Extensions Library, Rx)。接着,RxJava 在 JVM 上实现了响应式编程。后来,在 JVM 平台出现了一套响应式编程规范,它定义了一系列编程接口和交互规范,并整合到了 Java 9 中。

除了解决上述问题,响应式编程库还额外关注如下几个方面:

  1. 可编排性(Composability)和可读性(Readability)
  2. 提供丰富的操作符(operators)来处理形如流的数据
  3. 在订阅(Subscribe)之前什么都不发生
  4. 背压(BackPressure)支持,简单来说,订阅者能够反向告知发布者生产内容速度的能力
  5. 高层次的抽象,从而达到并发无关的效果

RxJava 和 Reactor

RxJava

RxJava 是 Reactive Extensions 的 Java VM 实现,它用于通过使用可观察的序列来组成异步和基于事件的程序。

它扩展了观察者模式以支持数据/事件序列,并添加了运算符,使您可以声明性地将序列组合在一起,同时消除了对诸如多线程,同步,线程安全和并发数据结构之类的问题的担忧。

RxJava 是 Java 界响应式编程的先行者,因为是先有的 RxJava,才后有的 Java 版本的响应式编程规范,同时该规范定义时参考了 RxJava 的许多定义,RxJava 的早期版本最低支持 Java 6,官方版本直至 Java 9 中才被支持。

RxJava 最新版本 3.x,最低需要 Java 8+,官方请看 https://github.com/ReactiveX/RxJava

Reactor

Reactor 是一个用于 JVM 的完全非阻塞的响应式编程框架,具备高效的需求管理(即对 “背压(backpressure)”的控制)能力。它与 Java 8 函数式 API 直接集成,比如 CompletableFuture, Stream, 以及 Duration。它提供了异步序列 API Flux(用于[N]个元素)和 Mono(用于 [0|1]个元素),并完全遵循和实现了“响应式扩展规范”(Reactive Extensions Specification)。

Reactor 的 reactor-ipc 组件还支持非阻塞的进程间通信(inter-process communication, IPC)。 Reactor IPC 为 HTTP(包括 Websocket)、TCP 和 UDP 提供了支持背压的网络引擎,从而适用于微服务架构。并且完整支持响应式编解码(reactive encoding and decoding)。

Reactor 是第四代响应式框架,跟 RxJava 2 有些相似。Reactor 项目由 Pivotal 启动,以响应式流规范、Java8 和 ReactiveX 术语表为基础。它的设计是 Reactor 2(上一个主要版本)和 RxJava 核心贡献者共同努力的结果。

从设计概念方面来看,RxJava 有点类似 Java 8 Steams API。而 Reactor 看起来有点像 RxJava,不过这决不只是个巧合。这样的设计是为了能够给复杂的异步逻辑提供一套原生的具有 Rx 操作风格的响应式流 API。所以说 Reactor 扎根于响应式流,同时在 API 方面尽可能地与 RxJava 靠拢。

Reactor 最新版本3.x,最低需要 Java 8+,官方请看 https://github.com/reactor/reactor-core

Java Stream Vs RxJava Vs Reactor

我们在前传中首先学习了Java Stream,通过上面笔记的介绍,发现 Java Stream 在很多方面与响应式编程方面相似,那么他们到底有哪些区别呢,来看徐靖峰在【八个层面比较 Java 8, RxJava, Reactor】中的下面这张图:

由于上述文章已经讲解的很好了,请大家跳转过去学习一下。

总结

RxJava 在 Android 开发界可算是如火如荼,通过事件的响应配合界面的操作可谓如鱼得水。Reactor 直至 Spring 5 中引入了 Reactive Stream 及 Spring WebFlux 才进入了我的视线,RxJava 的学习内容基本遍地都是了,而 Reactor 的内容却少之又少,这也是本片笔记的由来。

响应式编程的理论部分,总算是介绍完了。理论知识一直是我的弱势,上面的大部分内容都是源自 Reactor 的官方文档及下方各个参考文档,感兴趣的朋友可到对应的文章学习了解下。

今天的内容就讲到这里,我们下篇开始 Reactor 的学习。

参考

  1. 响应式规范
  2. Reactor 3 中文指南
  3. 这可能是最好的RxJava 2.x 教程(完结版)
  4. 重新理解响应式编程
  5. 八个层面比较 Java 8, RxJava, Reactor

学习响应式编程 Reactor (1) - 响应式编程的更多相关文章

  1. 学习响应式编程 Reactor (3) - reactor 基础

    Reactor Reactor 项目的主要 artifact 是 reactor-core,这是一个基于 Java 8 的实现了响应式流规范的响应式库. Reactor 提供了实现 Publisher ...

  2. html响应式布局,css响应式布局,响应式布局入门

    html响应式布局,css响应式布局,响应式布局入门 >>>>>>>>>>>>>>>>>>& ...

  3. Java并发编程系列-(4) 显式锁与AQS

    4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关 ...

  4. 总结切面编程AOP的注解式开发和XML式开发

    有段日子没有总结东西了,因为最近确实有点忙,一直在忙于hadoop集群的搭建,磕磕碰碰现在勉强算是能呼吸了,因为这都是在自己的PC上,资源确实有点紧张(搭建过程后期奉上),今天难得大家都有空(哈哈哈~ ...

  5. SQL反模式学习笔记19 使用*号,隐式的列

    目标:减少输入 反模式:捷径会让你迷失方向 使用通配符和未命名的列能够达到减少输入的目的,但是这个习惯会带来一些危害. 1.破坏代码重构:增加一列后,使用隐式的Insert插入语句报错: 2.查询中使 ...

  6. 编程中的链式调用:Scala示例

    编程中的链式调用与Linux Shell 中的管道类似.Linux Shell 中的管道 ,会将管道连接的上一个程序的结果, 传递给管道连接的下一个程序作为参数进行处理,依次串联起N个实用程序形成流水 ...

  7. SQL Server 学习博客分享列表(应用式学习 + 深入理解)

    SQL Server 学习博客分享列表(应用式学习 + 深入理解) 转自:https://blog.csdn.net/tianjing0805/article/details/75047574 SQL ...

  8. python高级编程之列表推导式

    1. 一个简单的例子 在Python中,如果我们想修改列表中所有元素的值,可以使用 for 循环语句来实现. 例如,将一个列表中的每个元素都替换为它的平方: >>> L = [1, ...

  9. 20121124.Nodejs异步式I/O与事件式编程

    异步: 你请人吃饭,准备一起去的.结果那人刚好有事,让你先去点菜,你去点好菜,他忙完就来了,这就是异步的优势(不耽误事!)同步: 就是,你必须等那个人忙完了,才一起去(浪费时间) 理解来源于群友&qu ...

随机推荐

  1. Truncate用法详解

    前言: 当我们想要清空某张表时,往往会使用truncate语句.大多时候我们只关心能否满足需求,而不去想这类语句的使用场景及注意事项.本篇文章主要介绍truncate语句的使用方法及注意事项. 1.t ...

  2. Spring 注解动态数据源设计实践

    Spring 动态数据源 动态数据源是什么?解决了什么问题? 在实际的开发中,同一个项目中使用多个数据源是很常见的场景.比如,一个读写分离的项目存在主数据源与读数据源. 所谓动态数据源,就是通过Spr ...

  3. Linux netperf网络性能测试

    Linux netperf网络性能测试 (2013-10-14 16:07:48) 转载▼     网络性能测量的五项指标 1. 可用性(availability) 测试网络性能的第一步是确定网络是否 ...

  4. 微信收藏了很多语音,有一些比较有意义的,但是发现只能收藏在微信,没有办法导出了,请大神看清楚,是微信【收藏】的语音,ios或者安卓的方法都可以

  5. "sar"工具 利用率

    LTP--linux稳定性测试 linux性能测试 ltp压力测试   余二五 2017-11-14 16:20:00 浏览1172 linux 日志 配置 内存管理 测试 脚本 性能测试 压力测试 ...

  6. MyBatis 回顾 JDBC(一)

    引言 学过 Java 的童鞋都知道,在 Java 中只有 JDBC 可以访问数据库,但是只要使用过 JDBC 的同学肯定也感受到 JDBC 访问数据库的繁琐, 需要编写大量的代码,经历一系列的步骤. ...

  7. 5.6 date:显示与设置系统时间

    date命令 用于显示当前的系统时间或设置系统时间. date [选项] +[日期格式]   date命令的参数选项及说明 OPTION参数选项-d 时间字符串 显示指定字符串所描述的时间,而非当前时 ...

  8. FOC_矢量控制相关资料

    http://www.eepw.com.cn/news/listbylabel/label/FOC/ FOC(field-oriented control)为磁场导向控制,又称为矢量控制(vector ...

  9. 大师画PCB板子

    1.低频电路对于模拟地和数字地要分开布线,不能混用 2.如果有多个A/D转换电路,几个ADC尽量放在一起,只在尽量靠近该器件处单点接地,AGND和DGND都要接到模拟地,电源端子都要接到模拟电源端子: ...

  10. centos下yum方法安装apache+php+mysql

    yum(全称为:Yellow dog Updater,Modified) 是一个在Fedora和RedHat以及SUSE中的Shell前端管理软件.基于RPM包管理,能够从远处镜像服务器下载RPM包并 ...