概念

Reactive Programming(响应式编程)已经不是一个新东西了。

关于 Reactive 其实是一个泛化的概念,由于很抽象,一些理论性的介绍很容易把人带到沟里去,包括一些语言框架在实现上也会使用不同的一些概念。

按照 维基百科的解释:

reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change

意思就是,Reactive Programming 就是一种面向数据流、关注变更的声明式编程范式。

面向数据流比较容易理解,而关注变更则说的应该是数据流的特点,比如来自某个界面元素属性的变更(前端领域)、又或是某个后端实体的更新事件(日志)..

以下面的这个函数为例:

c = a + b;

这里定义了变量c 是 变量a、变量b 之和,当a=1,b=2时,c的值就是3。

假设我们在程序中执行了这个语句,那么对于一次执行过程所产生的c的值就是确定的(上下文中的a、b变量也是确定的)

但是,如果a、b的值是不确定的呢?即这个语句仅仅是定义了变量c与 变量a、b 的计算关系,那么c 的值就是可变的!

如下:

a=1,b=1,c=2
a=2,b=2,c=4
a=3,b=2,c=5
...

简言之,c需要动态的由 a、b 共同来决定:

当 a、b 的值发生变化时,c 的结果要能及时的做出响应(或者叫反应),以此来保证正确性。

这应该就是 Reactive(响应式) 的由来了,由于变量 a、b的值可能会不断的变化,于是会形成持续不断的变更事件,也就是事件流,因此 Reactive 是面向流式处理来设计的。

此外,在处理这种"变更的流"时,通常是由异步通知的方式来完成,因此异步化也是其特征之一。

从现有的一些Reactive框架来看,关于下面的定义则更加的贴切:

Reactive编程 是面向数据流的、异步化的编程范式

图-Reactive-Proactive

与Reactive 相对的是Proactive ,后者是一种同步的、轮询式的处理方式

面向流设计

首先,有别于面向对象编程的思想,在Reactive 范式里面,所有的东西都可以当做流,即 Everything is Stream。

流(Stream) 被作为响应式编程的基本元素,这和其他的编程范式非常类似:

  • 面向对象设计,基本单位是对象
  • 面向函数设计,基本单位就是函数
  • 响应式设计,基本单位就是流..

那么流是什么样的东西呢?

可以是 用户输入、数据结构、缓存、动态变量... 等等!

可以来自 静态的数据集合,或是动态的事件流。

案例:MVC

MVC(Model-View-Controller) 是前端设计的标准,这也是用来说明"面向流"的一个很好的例子。

图-MVC

其中,来自于用户的点击操作,会被转换为各种事件传递给 Controller 进行处理。

在这里,我们可以认为这些持续不断的事件形成了"事件流"。 比如一个按钮的点击事件流如下图:

在这里,事件流是按时间排序进行处理的。 但你可能会说,这不就是简单的一个事件处理机制嘛?

别着急,基于响应式流可以做更多的事情,如下图:

上图的每个灰框代表了一个处理方法:

  • buffer(stream.throttle(250ms)),buffer就是缓冲,而throttle 是节流。

    这个函数的意思就是对流进行缓冲处理,将250毫秒范围内发生的事件合并到一起。
  • map('length of list'),将合并后的列表进行转换,输出为每个列表的长度
  • filter(x>=2),即按照>=2的条件进行过滤。

当然,使用传统的编程方式也完全可以实现这些逻辑,只是相比之下基于响应式流的处理会更加的优雅,所用代码也会更少。

上面的这个例子出自于《The introduction to Reactive Programming you've been missing》(英文原文:https://gist.github.com/staltz/868e7e9bc2a7b8c1f754),该文章也获得非常多的star,至少有一部分可以说明基于MVC的例子来理解响应式还是比较容易的。当然,除了前端领域之外,也很容易将响应式流的思想扩展到各个方面,包括 Web后端、大数据处理、实时流计算等等。

异步化

异步化处理是响应式编程的另一个重要特征,这里的异步与我们常说的网络IO异步化意思上是相同的,与异步相对的概念是同步。

下面的案例可以很好的解释两者的区别:


假设你是一个读书爱好者,某一天你想看《开国大典》这本书,于是你打电话给图书馆的管理员,询问馆内是否有这本书可以借。 同步的方式,管理员在接到电话之后让你等一下,然后去图书室查找一番,几分钟后回来再拿起电话告诉你结果;
异步的方式,管理员把你的电话号码记下来,然后挂掉电话,后面他查找完了再打回电话给你通知结果。

同步和异步的区别就在于结果通知的方式不同,很明显,异步的方式会显得更加的人性化和高效。

因此,响应式编程通常是采用异步回调的方式,回调方法的调用和控制则会由响应式框架来完成,对于应用开发来说只需要关注回调方法的实现就可以了。

关于同步、异步,往往会牵扯到阻塞、非阻塞 这两个相似的概念,需注意的是 后者的侧重点不同:

阻塞、非阻塞所关注的是调用者的状态(是否可以停下来做其他事情)的区别

既然谈到了异步,这里提一个著名的设计原则:好莱坞原则(Hollywood principle)

don't call us, we'll call you

不要给我们打电话,我们会给你打电话

在好莱坞,把简历递交给演艺公司后就只有回家等待。由于演艺公司对整个娱乐圈是完全控制的,演员只能被动式的接受公司的差使,只能在需要的环节中完成自己的演出。

好莱坞原则的核心是以通知代替轮询,其强调的是使用回调来降低模块间的依赖关系,或是提升消息处理效率。

与好莱坞原则相关(延伸)的设计模式有许多:

  • Spring 的依赖注入(DI),通过将Bean的定义、依赖关系配置到XML文件中,由容器来完成Bean的自动装配。

    这样控制权就从具体的 Bean转移到到了容器手上,于是就有了控制反转IoC(Inversion of Control)一词。

  • Swing UI框架中大肆使用的 观察者模式(Observer), 我们希望获知某个UI组件的事件变化,可以添加一个ActionListener。

    之后Swing将会自动将发生的事件传递到我们的回调方法上(actionPerformed)。

  • Reactor 响应器模式,基于事件驱动的一种设计模式,其设定了Service Handler负责派发事件,Service Handler同步获得输入的事件后,进而分发给相应的Request Handler(多路复用)

    Reactor 一般是用于NIO的场景,如Netty 的网络处理模型:

注意到了吗?这些设计模式都不约而同使用了回调!,当然在Reactive 范式中也必然离不开这点。

或许,100 种设计模式中,调整一下角度,可以归纳为10种甚至更少。

响应式宣言

https://www.reactivemanifesto.org/

除了上述的两大特征之外,还需要提到的一个东西叫 Reactive Manifesto(响应式宣言),这个是由Lightbend 公司发起的。 它的前身是Typesafe,大名鼎鼎的Scala 就是其发明的。 还有流行的Web后端框架 Playframework 也出自于此。

Playframework 的底层是基于Scala的(可同时支持Java和Scala开发),同时也包含了NIO、Reactive的各种特性,不少国外的企业如Linkin、Verizon 都在使用。

于是,有了响应式宣言之后,Reactive开始得到了正名,随后的Akka、Rx系列、包括Spring生态 都纷纷加入了这个队列。

在这个宣言里面,对于响应式的系统特征定义了四个特性:

  • 及时响应(Responsive):系统能及时的响应请求。
  • 有韧性(Resilient):系统在出现异常时仍然可以响应,即支持容错。
  • 有弹性(Elastic):在不同的负载下,系统可弹性伸缩来保证运行。
  • 消息驱动(Message Driven):不同组件之间使用异步消息传递来进行交互,并确保松耦合及相互隔离。

在响应式宣言的所定义的这些系统特征中,无一不与响应式的流有若干的关系,于是乎就有了 2013年发起的 响应式流规范(Reactive Stream Specification)

https://www.reactive-streams.org/

其中,对于响应式流的处理环节又做了如下定义:

  • 具有处理无限数量的元素的能力,即允许流永不结束
  • 按序处理
  • 异步地传递元素
  • 实现非阻塞的负压(back-pressure)

负压这个概念或许有些陌生,但本质是为了协调流的处理能力提出的,对于流处理来说会分为 Publisher(发布者) 和Subscriber(订阅者)两个角色,可看做生产者与消费者的模式。当发布者产生的消息过快时,订阅者的处理速度可能会跟不上,此时可能会导致一系列的系统问题。 因此负压的目的就是定义一种反馈机制,让订阅者(消费方)向发布者告知其自身的状态(包括处理速度),

尽可能让发布方作出调整,本质上是一种系统自我保护的手段。 说到这里,不得不想到TCP的 MTU协商了。

Java 9 平台开始支持 Reactive Stream API

关于Reactive Stream 规范的定义可以参考这篇翻译:

https://github.com/yelf2000/rxjava/wiki/Reative-Streams-规范

为什么要使用Reactive

回答这个问题并不容易,一定是要从 Reactive 编程中获得一定好处了之后才能解答,当然不同人的看法也不一样。

就笔者浅显的看法来说,Reactive响应式编程提出了一种更高级的抽象,将数据的处理方式沉淀到可复用的库之后可以提高开发的效率。

实质上, Reactive响应式始终是一种模式,只是在不同的框架体系中产生了各种五花八门的说法,导致初学者非常容易迷路。

光是Java语言中的 RxJava、Reactor、Java 9 这些不同类库的接口概念就有不少差异,更不用说跨语言了。

对此,下面的这篇文章有比较详细的解读,值得一看:

https://yq.aliyun.com/articles/617709

参考文档

极客学院译文-响应式编程介绍

https://wiki.jikexueyuan.com/project/android-weekly/issue-145/introduction-to-RP.html

Java 平台 Reactive编程

https://cloud.tencent.com/developer/article/1073888

the-hollywood-principle(好莱坞原则)

https://dzone.com/articles/the-hollywood-principle

维基百科- Reactive Programing

https://en.wikipedia.org/wiki/Reactive_programming

Reactive 漫谈的更多相关文章

  1. 【道德经】漫谈实体、对象、DTO及AutoMapper的使用

    写在前面 实体和值对象 实体和对象 故常无欲以观其妙,常有欲以观其徼 初始实体和演化实体 代码中的DTO AutoMapper实体转换 后记 实体(Entity).对象(Object).DTO(Dat ...

  2. CSS实现水平|垂直居中漫谈

    利用CSS进行元素的水平居中,比较简单,手到擒来:行级元素设置其父元素的text-align center,块级元素设置其本身的left 和 right margins为auto即可.而撸起垂直居中, ...

  3. 【转】漫谈iOS程序的证书和签名机制

    转自:漫谈iOS程序的证书和签名机制 接触iOS开发半年,曾经也被这个主题坑的摸不着头脑,也在淘宝上买过企业证书签名这些服务,有大神都做了一个全自动的发布打包(不过此大神现在不卖企业证书了),甚是羡慕 ...

  4. UP board 漫谈(1)——从Atom到UP Board

    title: UP board 漫谈(1)--从Atom到UP Board date: 2016-12-26 12:33:03 tags: UP board categories: 开发板 perma ...

  5. Reactive Extensions(Rx) 学习

    Bruce Eckel(著有多部编程书籍)和Jonas Boner(Akka的缔造者和Typesafe的CTO)发表了“反应性宣言”,在其中尝试着定义什么是反应性应用. 这样的应用应该能够: 对事件做 ...

  6. Reactive Extensions介绍

    Reactive Extensions(Rx)是对LINQ的一种扩展,他的目标是对异步的集合进行操作,也就是说,集合中的元素是异步填充的,比如说从Web或者云端获取数据然后对集合进行填充.Rx起源于M ...

  7. .Net中的反应式编程(Reactive Programming)

    系列主题:基于消息的软件架构模型演变 一.反应式编程(Reactive Programming) 1.什么是反应式编程:反应式编程(Reactive programming)简称Rx,他是一个使用LI ...

  8. 漫谈C++11 Thread库之原子操作

    我在之前一篇博文<漫谈C++11 Thread库之使写多线程程序>中,着重介绍了<thread>头文件中的std::thread类以及其上的一些基本操作,至此我们动手写多线程程 ...

  9. 基于移动端Reactive Native轮播组件的应用与开发详解

    总结下这段时间学习reactive native的一些东西,我们来认识一下,被炒得这么火的rn,究竟是个什么东西,以及如何去搭建自己的demo. reactive  native是什么 由facebo ...

随机推荐

  1. 详叙BeanWrapper和PropertyDescriptor

    每篇一句 千古以来要饭的没有要早饭的,知道为什么吗? 相关阅读 [小家Spring]聊聊Spring中的数据转换:Converter.ConversionService.TypeConverter.P ...

  2. 【动态规划例题-数塔问题】-C++

    描述 观察下面的数字金字塔.写一个程序查找从最高点到底部任意处结束的路径,使路径经过数字的和最大.每一步可以 从当前点走到左下方的点也可以到达右下方的点. 在上面的样例中,从13到8到26到15到24 ...

  3. 洛谷P3877 [TJOI2010]打扫房间 解题报告

    首先整理一下条件: 1.恰好进出每个需打扫的房间各一次 2.进出每个房间不能通过同一个门 (其实前两个条件是一回事) 3.要求每条路线都是一个闭合的环线 4.每条路线经过的房间数大于2 让你在一个n* ...

  4. 2019年7月22日 - LeetCode0004

    https://leetcode-cn.com/problems/median-of-two-sorted-arrays/submissions/ 我的解法: 我看到了那个log的要求,也第一时间想到 ...

  5. vijos P1848 记数问题

    自答[119ms内存456.0 KiB] #include<iostream>using namespace std;int num = 0;void judge(int n, int x ...

  6. [leetcode] 8. String to Integer (atoi) (Medium)

    实现字符串转整形数字 遵循几个规则: 1. 函数首先丢弃尽可能多的空格字符,直到找到第一个非空格字符. 2. 此时取初始加号或减号. 3. 后面跟着尽可能多的数字,并将它们解释为一个数值. 4. 字符 ...

  7. 使用nginx+tomcat实现动静分离

    动态资源与静态资源的区别 微微的概括一下 静态资源: 当用户多次访问这个资源,资源的源代码永远不会改变的资源. 动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变. 什么是动静分离 动静分离 ...

  8. IP地址/掩码/网关/DNS的设置与工作原理(转)

    现在互联网飞速发展,工作中生活中上不了网的计算机是不可想象的,而电脑系统网络设置中的IP地址.子网掩码.默认网关及DNS服务器,怎么理解,它们是如何工作的,下面做个简单介绍. 通常的上网方式,是打开I ...

  9. Atlassian In Action-Jira之推荐插件(四)

    前面的几章基本已经完整构建了Jira的管理平台,并且有了一套比较完成的制度和方法.但是优化是永无止境的,我们作为研发管理人员,需要让系统使用起来更加高效和便捷.为了达到这个目的一般有两种途径,插件和开 ...

  10. Angular JS 中的内置方法之表单验证

    angular js 结合html5 可以实现强大的表单验证功能 关闭html5自带的表单验证功能可以用