响应式编程
简介
响应式编程(Reactive Programming)是基于数据流(Stream)模式的一种编程范式,编程范式不和特定的编程语言,编程框架甚至应用场景关联,而是更加通用的一种编程思想。常见的编程范式有命令式编程,声明式编程,函数式编程,面向对象编程,事件驱动编程。
编程范式 | 概念 | 特点 |
---|---|---|
命令式编程 | 通过调用函数(方法)改变数据的状态完成程序目标。 | 简单,是目前主流的编程范式。
命令式编程一般用于同步式调用。 |
声名式编程 | 声明式的编程模型分离了逻辑的表达和实现,该范式偏重于逻辑表达。
这部分表达可能是通过DSL等描述,也可能是通过事先设定的规范进行描述。 |
声名的具体实现往往通过其他编程范式实现。 |
函数式编程 | 函数作为函数式编程的基本类型,可以作为参数输入以及返回。
通过函数之间的级联完成业务功能。 |
不修改变量的状态,所有变量的声明周期只在函数内部,所以没有副作用。 |
面向对象编程 | 通过调用对象的方法,修改对象的属性实现业务逻辑。 | 适合复杂的业务场景。 |
事件驱动编程 | 事件是指系统中发生的事件。
在MVC中可能是用户的点击事件,在Server中可能是IO事件。 通过定义不同的监听器监听这些事件完成业务功能。 |
简单高效,但是业务流程碎片化。 |
响应式编程 | 基于数据流,和事件驱动模式同属于异步编程范式。
不同的是响应式编程不监听具体事件,而直接对最终的数据进行响应。 例如MVVM将服务端数据通过提前定义的ViewModel直接展示在视图上。 |
原理
和所有的编程范式类似,响应式编程也可以分为输入,逻辑实现,输出三个组件。
- 在响应式编程中,输入一般通过数据流(Stream来表达)
- 响应式逻辑的逻辑实现一般通过函数式编程范式来实现。
- 响应式编程的输出一般通过观察者模式监听并且将这些数据的影响进行传播,例如前端常用的状态管理模块可以将数据和视图建立映射。
数据流
不同于命令式编程中,等执行逻辑结束之后返回输出之后,命令的生命周期就结束了。
响应式编程将数据封装为流式数据,数据流是源源不断的。生产者不停的产生新的数据,生产者和数据流的消费者之间通过推或者拉的方式进行交互。
生产者和消费者会出现速度不匹配的问题,分为两种情况:
- 消费速度快于生产速度
- 消费速度较快的时候会出现消费者等待生产者的情况,如果是同步模式,消费者可以进行阻塞,异步模式情况下,消费者会进入挂起状态,等待新的数据流的到来。
- 消费速度较快的时候,为了有更好的响应能力,一般采用推模式进行消费。
- 生产速度快于消费速度
- 消费能力不足的情况,为了匹配消费速度有两种方式,消费者可以丢弃数据或者暂存数据。
- 背压( backpressure )的方式由消费者发出信号通知生产者降低信息的发布速度,从而让信息速度之间匹配,采用背压策略之后,丢弃数据或者暂存数据的策略由生产者实现。
响应式流(Reactive Streams) 是一项非阻塞背压的异步流处理标准,在推(Push)拉(Pull)流处理机制之间可以动态切换。当消费者较慢时,消费者来控制消费速度,生产者会进入等待请求信息的状态;消费速度更快的时候,消费者进入到等待消息发送的状态。
响应式流的模型如下:
- 订阅者异步的向发布者请求N个元素。
- 发布者一步的向订阅者发送M(0<M<=N)个元素。
处理器
处理数据流有两种方式,
- 主动轮训( Proactive) 方式。
- 事件驱动( Reactive)方式,Reactive 方式中,事件驱动处理逻辑,而且事件可能会经过多个处理器,上一个处理器处理事件之后的结果会作为下一个处理器的事件。
响应式编程中使用 Reactive 的方式处理事件,具体的处理器称为 Reactor,Reactor接受事件并处理,事件处理完成之后会生成另外一个事件给下游的 Reactor。Reactors 之间通过消息管道来传递消息。
状态管理
状态管理是数据流向的终点。
数据流经过处理器之后,消费者(Sink)会将这些结果进行展示或者存储。
Sink处理数据的模式也是异步的和自动化的,一般情况下通过Observer监听的方式处理数据。
特点
反应式系统有即时响应性(Responsive)、回弹性(Resilient)、弹性(Elastic)以及消息驱动(Message Driven)等特征。