反应式宣言

反应式宣言

反应式宣言 是一切反应式概念的根源理论基础。描述了反应式系统(reactive systems)应该具备的四个关键属性:Responsive(灵敏的)、Resilient(可故障恢复的)、Elastic(可伸缩的)、Message Driven(消息驱动的)。

反应式因素

  • Responsive(灵敏的):只要有可能,系统就会及时响应。灵敏性是系统可用性的基石,除此之外,灵敏性也意味着系统的问题可以被快速地探测和解决。具有灵敏性的系统关注做出快速和一致的响应,提供可靠和一致的服务质量。

  • Resilient(可故障恢复的):在出现故障时,系统仍然可以保持响应。一个不具可恢复性的系统一旦出现故障,就会变得无法正常响应。可恢复性可以通过复制、围控、隔离和委派等方式实现。在可恢复性的系统中,故障被包含在每个组件中,各组件之间相互隔离,从而允许系统的某些部分出故障并且在不连累整个系统的前提下进行恢复。

  • Elastic(可伸缩的):在不同的工作负载下,系统保持响应。系统可以根据输入的工作负载,动态地增加或减少系统使用的资源。这意味着系统在设计上可以通过分片、复制等途径来动态申请系统资源并进行负载均衡,从而去中心化,避免节点瓶颈。

  • Message Driven(消息驱动的):反应式系统依赖异步消息传递机制,从而在组件之间建立边界,这些边界可以保证组件之间的松耦合、隔离性、位置透明性,还提供了以消息的形式把故障委派出去的手段。

  • Failures at messages:在 Reactive 编程中,我们通常需要处理流式的信息,我们最不希望看到的是突然抛出一个异常,然后处理过程终止了。理想的解决办法是我们记下这个错误,然后开始执行某种重试或恢复的逻辑。在 Reactive Streams 中,异常是一等公民,异常不会被粗鲁地抛出,错误处理是正式建立在 Reactive Streams API 规范之内的。

  • Back-pressure:中文一般翻译成“背压”、“回压”,意思是当消费端的消费能力跟不上生产端的生产速度时,消息流下游的消费方对上游的生产方说:“我喝饱了,请你慢点”。在 Reactive 的世界里,我们希望下游的消费方可以有某种机制按需请求一定数量的消息来消费(这类似消息队列中的 pull 的概念)。而不是上游把大量的消息一股脑灌给下游消费方,然后阻塞式等待,throttling(节流) is done programmatically rather than blocking threads。

  • Non-blocking:数据处理以一种非阻塞的方式被处理,线程不会因为等待别的处理过程而卡住。这里可以对比有着 非阻塞事件循环 的 Node.js Server(如一条高速公路)和传统的 Java 多线程服务(如拥有红绿灯的十字路口)

反应式编程的构成

Stream(流)

作为响应式编程的核心,流的本质是一个按时间顺序排列的进行中事件的序列集合。它可以发送三种不同的事物:

  • 某种类型的值
  • 错误(Error)
  • 已完成信号(Completed Signal)

下图是点击按钮的一个事件流示意图:

![https://s1.ax1x.com/2020/03/26/GSZ4GF.png]

--a---b-c---d---X---|->

a, b, c, d are emitted values
X is an error
| is the 'completed' signal
---> is the timeline

我们可以针对它做一些处理,将它转化为一个新的 stream,比如做一个能记录一个按钮点击了多少次的计数器 Stream。在常见的响应式编程库中,每个 stream 都会有多个方法,map、filter、scan 等等。当你调用其中一个方法时,例如 clickStream.map(f),它就会基于原来的 click stream 返回一个新的 stream。它不会对原来的 click steam 作任何修改。这个特性就是不可变性(Immutability),我们也可以对方法进行链式调用如 clickStream.map(f).scan(g).

clickStream: ---c----c--c----c------c-->
               vvvvv map(c becomes 1) vvvv
               ---1----1--1----1------1-->
               vvvvvvvvv scan(+) vvvvvvvvv
counterStream: ---1----2--3----4------5-->

为了展示 RP 真正的实力,让我们假设你想得到一个包含双击事件的 Stream。为了让它更加有趣,假设我们想要的这个 Stream 要同时考虑三击 (Triple clicks),或者更加宽泛,连击 (Multiple clicks)。我们先不讨论它的实现,用示意图来表示这个 stream 它应该长成这样:

Multiple Clicks Stream

观察者模式

观察者模式又叫发布订阅模式(Publish/Subscribe),它是一种一对多的关系,让多个观察者(Obesver)同时监听一个主题(Subject),这个主题也就是被观察者(Observable),被观察者的状态发生变化时就会通知所有的观察者,使得它们能够接收到更新的内容。观察者模式主题和观察者是分离的,不是主动触发而是被动监听。

迭代器模式

迭代器(Iterator)模式又叫游标(Sursor)模式,迭代器具有 next 方法,可以顺序访问一个聚合对象中的各个元素,而不需要暴露该对象的内部表现。迭代器模式可以把迭代的过程从从业务逻辑中分离出来,迭代器将使用者和目标对象隔离开来,即使不了解对象的内部构造,也可以通过迭代器提供的方法顺序访问其每个元素。

例如在 JavaScript 中,可以通过 iterator 方法来获取一个迭代对象,然后调用迭代对象的 next 方法去迭代得到一个个的元素:

var iterable = [1, 2];
var iterator = iterable[Symbol.iterator]();
iterator.next(); // => { value: "1", done: false}
iterator.next(); // => { value: "2", done: false}
iterator.next(); // => { value: undefined, done: true}