Reactive(1) 從響應式編程到"好萊塢"

概念

Reactive Programming(響應式編程)已經不是一個新東西了。
關於 Reactive 實際上是一個泛化的概念,因爲很抽象,一些理論性的介紹很容易把人帶到溝裏去,包括一些語言框架在實現上也會使用不一樣的一些概念。前端

按照 維基百科的解釋:java

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

意思就是,Reactive Programming 就是一種面向數據流、關注變動的聲明式編程範式。
面向數據流比較容易理解,而關注變動則說的應該是數據流的特色,好比來自某個界面元素屬性的變動(前端領域)、又或是某個後端實體的更新事件(日誌)..android

如下面的這個函數爲例:git

c = a + b;

這裏定義了變量c 是 變量a、變量b 之和,當a=1,b=2時,c的值就是3。
假設咱們在程序中執行了這個語句,那麼對於一次執行過程所產生的c的值就是肯定的(上下文中的a、b變量也是肯定的)
可是,若是a、b的值是不肯定的呢?即這個語句僅僅是定義了變量c與 變量a、b 的計算關係,那麼c 的值就是可變的!github

以下:編程

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-%E8%A7%84%E8%8C%83

爲何要使用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

相關文章
相關標籤/搜索