反应式编程详解
转载:
反应式编程详解
| 导语反应式编程是在命令式编程、⾯向对象编程之后出现的⼀种新的编程模型,是⼀种以更优雅的⽅式,通过异步和数据流来构建事务关系的编程模型。本⽂包括反应式编程的概述和 RxPy 实战,以及怎样去理解反应式编程才能更好的把它融⼊到我们的编程⼯作中,把反应式编程变成我们⼿中的利器。
1. 反应式编程概述
1.1 背影趋势
在 google 趋势中搜索反应式编程,可以看到其趋势在 2013 年后⼀直是往上⾛的。如图1所⽰:
[ 图1 google 趋势搜索结果 ]
为啥呢?为啥是 2013 年才有明显的变化,因为2013 年后才有可以⼤范围使⽤的框架和库出现,才有⼈专
门投⼊去布道反应式编程这个事情。
在范围缩⼩到中国,这个结果有点意思了,如图 2 所⽰:
[ 图2 google趋势搜索结果 ]
在中国主要是北上⼴深和杭州,说明什么,这些技术还是⼀线城市的开发同学才会使⽤,查看左下⾓主要是主题都是java相关,查看右上⾓,浙江省⽤得⽐较多,说明阿⾥是主要的使⽤⽅。
1.2 定义
反应式编程⼜叫响应式编程,在中,其属于声明式编程,数据流。
其定义为:
反应式编程 (reactive programming) 是⼀种基于数据流 (data stream) 和变化传递 (propagation of change) 的声明式
(declarative) 的编程范式。
换句话说:使⽤异步数据流进⾏编程,这意味着可以在编程语⾔中很⽅便地表达静态或动态的数据流,⽽相关的计算模型会⾃动将变化的值通过数据流进⾏传播。
反应式编程提⾼了代码的抽象级别,可以只关注定义了业务逻辑的那些相互依赖的事件。
1.3 Rx的发展
反应式编程最着名的实现是 ReactiveX,其为 Reactive Extensions 的缩写,⼀般简写为 Rx ,发展历程如图 3 所⽰:
[ 图3 Rx来历 ]
微软 2009 年以 .Net 的⼀个响应式扩展的⽅式创造了Rx,其借助可观测的序列提供⼀种简单的⽅式来创建异步的,基于事件驱动的程
序。2012 年 Netflix 为了应对不断增长的业务需求开始将 .NET Rx 迁移到 JVM 上⾯。并于 2013 年 2 ⽉份正式向外发布了 RxJava 。
1.4 反应式宣⾔
在 2014 年 9 ⽉ 16 号,反应式宣⾔正式发布了 2.0 版本。在 2.0 之前,这份宣⾔的中⽂翻译标题,实际上是”响应式宣⾔“,⽽⾮”反应式宣⾔“
在反应式宣⾔中的 ”Reactive“ 实际上是指⼀个副词,表⽰系统总是会积极主动、甚⾄是智能地对内外的变化做出反应。所以这⾥叫反应式编程会更贴切⼀些.
反应式宣⾔是⼀份构建现代云扩展架构的参考⽅案框架。这个框架主要使⽤消息驱动的⽅法来构建系统,在形式上可以达到弹性和回弹性,最后可以产⽣即时响应性的价值。如图 4 所⽰:
[ 图4 反应式编程 ]
反应式系统具有如图所⽰的4个特性:
1. 即时响应性,对⽤户有反应:对⽤户有反应我们才说响应,⼀般我们说的响应,基本上都说得针对跟⽤户来交互。只要有可能,系统
就会及时响应。
2. 回弹性,对失败有反应:应⽤失败了系统不能⽆动于衷,不能等着它挂掉,要有反应,使其具备可恢复性。可恢复性可以通过复制、
监控、隔离和委派等⽅式实现。在可恢复性的系统中,故障被包含在每个组件中,各组件之间相互隔离,从⽽允许系统的某些部分出故障并且在不连累整个系统的前提下进⾏恢复。当某个模块出现问题时,需要将这个问题控制在⼀定范围内,这便需要使⽤隔绝的技术,避免雪崩等类似问题的发⽣。或是将出现故障部分的任务委托给其他模块。回弹性主要是系统对错误的容忍。
3. 弹性,对容量和压⼒变化有反应:在不同的⼯作负载下,系统保持响应。系统可以根据输⼊的⼯作负载,动态地增加或减少系统使⽤
的资源。这意味着系统在设计上可以通过分⽚、复制等途径来动态申请系统资源并进⾏负载均衡,从⽽去中⼼化,避免节点瓶颈。如果没有状态的话,就进⾏⽔平扩展,如果存在状态,就使⽤分⽚技术,将数据分⾄不同的机器上。
4. 消息驱动,对输⼊有反应:响应系统的输⼊,也可以叫做消息驱动。反应式系统依赖异步消息传递机制,从⽽在组件之间建⽴边界,
这些边界可以保证组件之间的松耦合、隔离性、位置透明性,还提供了以消息的形式把故障委派出去的⼿段。
前三种特性(即时响应性, 回弹性, 弹性)更多的是跟你的架构选型有关,我们可以很容易理解像 Microservices、Docker 和 K8s 这样的技
术对建⽴反应式系统的重要性。
1.5 回压
这⾥要特别要提⼀下回压(Backpressure), Backpressure 其实是⼀种现象,在数据流从上游⽣产者向下游消费者传输的过程中,上游⽣产速度⼤于下游消费速度,导致下游的 Buffer 溢出,这种现象就叫做 Backpressure 出现。这句话的重点不在于”上游⽣产速度⼤于下游消费速度”,⽽在于”Buffer 溢出”。回压和 Buffer 是⼀对相⽣共存的概念,只有设置了 Buffer,才有回压出现;只要设置了 Buffer,⼀定存在出现回压的风险。
⽐如我们开发⼀个后端服务,有⼀个 Socket 不断地接收来⾃⽤户的请求来把⽤户需要的数据返回给⽤户。我们服务所能承受的同时访问⽤户数是有上限的,假设最多只能承受 10000 的并发,再多的话服务器就有当掉的风险了。对于超过 10000 的⽤户,程序会直接丢弃。那么对于这个案例 10000 就是我们设置的 Buffer,当超过 10000 的请求产⽣时,就造成了回压的产⽣;⽽我们程序的丢弃⾏为,就是对于回压的处理。
对于回压我们⼀般有两种处理⽅式,⼀种就是上⾯举例中的拒绝或丢弃,这是否定应答的⽅式,另⼀种是肯定应答,先收下来,然后再慢慢处理。
1.6 Rx适⽤场景
[图5 适⽤场景 ]
Rx 适⽤于前端,跨平台,后端等场景,其中在Angular 2.x,vue,react版本中已经有了Rx的实现可以使⽤,并且作为其核⼼的特性在宣传;Rx⽀持多达18种语⾔,在各平台都可以使⽤,具有很强的跨平台特性;在后端,通过异步调⽤,简单的并发实现,可以实现松耦合的架构。
1.7 哪些语⾔或框架⽀持反应式编程
18种语⾔Rx系统的框架出现⽐较早,已经发布了v2版本了,Rx* 系列语⾔⽀持如下:
Java: RxJava
JavaScript: RxJS
C#: Rx.NET
C#(Unity): UniRx
Scala: RxScala
Clojure: RxClojure
C++: RxCpp
Lua: RxLua
Ruby: Rx.rb
Python: RxPY
Go: RxGo
Groovy: RxGroovy
JRuby: RxJRuby
Kotlin: RxKotlin
Swift: RxSwift
PHP: RxPHP
Elixir: reaxive
Dart: RxDart
框架⽀持:
RxCocoa: RxCocoa是RxSwift的⼀部分,主要是UI相关的Rx封装
RxAndroid: RxAndroid 源于RxJava,是⼀个实现异步操作的库,具有简洁的链式代码,提供强⼤的数据变换。
RxNetty: RxNetty 是⼀个响应式、实时、⾮阻塞的⽹络编程库,基于 Netty 这个著名的事件驱动⽹络库的强⼤功能。⽀持
Tcp/Udp/Http/Https。⽀持>RxJava。RxNetty 在 NetFlix公司的各种产品中得到了⼴泛的应⽤。
Reactor: Reactor相对出⽣较晚,有发展前景Akka,scala系,⽤户基础薄弱
1.8. 哪些公司在⽤Rx
[ 图6 哪些公司在⽤Rx ]
2. RxRy⼊门
2.1 Rx组成
Rx的组成包括5部分,被观察者或者叫发射源,观察者/订阅者或者叫接收源,订阅,调度器,操作符。
Observable<Data> 被观察者可以被观察者订阅,被观察者将数据push给所有的订阅者
Subscriber /Observer
Subscription 订阅可以被取消订阅
Schedulers 调度器是Rx的线程池,操作中执⾏的任务可以指定线程池,我们可以通过subscribeOn来指定Observable的
任务在某线程池中执⾏Observable
也可以通过observeOn来指定订阅者/观察者们,在哪个线程执⾏onNext, onComplete, onError
Operators 操作符可以对数据流进⾏各种操作,包括创建,转换,过滤,组装,合并,筛选等等
javascript是什么意思中文翻译我们经常⽤如图7所⽰的⽰例图来表⽰数据流动的过程。
[ 图7 ]
图中上⾯这条线表⽰被观察者的时间线,表⽰输⼊,从左到右输⼊项,中间的各种颜⾊的块块是我们要观察的项,最后的竖线表⽰输⼊结束。
Flip是变换过程,对所有的项做变换。下⾯这条线是变换的结果,也就是输出,同样各种颜⾊的块块是要观察的结果的项,xx表⽰异常中断。
2.2 第⼀次体验Rx
需求如下:
从输⼊框获取输⼊,从第 10 次输⼊开始取前5次的输⼊,打印出来。
这是⼀个命令式编程的⽰例,我们需要将需求转换成命令式的描述,引⼊了计数变量,通过计数变量来跳过输⼊,然后再根据计算变量来标记取数的次数,打印出来,代码如图8所⽰:
[ 图8 ]
换成反应式编程,代码如图 9 所⽰:
[ 图9]
这是⼀个反应式的⾯向数据流的⽰例,创建流,跳过前 10 个项,取前5次,打印出来。如图 10 所⽰为其数据流动⽰例。
[ 图10 ]
图⽚来源:
github/ReactiveX/RxJava/wiki/How-To-Use-RxJava
对⽐命令式编程和反应式编程,区别如下:
1. 命令式编程,重视控制(执⾏过程),以运算、循环、条件判断、跳转来完成任务;计算机为先的思维,指令驱动机器做事;容易引
⼊⼤量状态变量
2. 反应式编程,重视任务的解决(执⾏结果),关注数据转换和转换的组合;⼈脑思维,任务驱动,分治;明确的输⼊和输出状态
Rx主要是做三件事:
1. 数据/事件的创建
2. 组合/转换数据流
3. 监听处理结果
下⾯我们以⽂档+代码的⽅式介绍这三件事情。
2.3 创建流
RxPy 有 10 种⽤于创建 Observable 的操作符,如下: