剖析響應式程式設計的本質
基於Actor的響應式程式設計計劃分為三部分,第一部分剖析響應式程式設計的本質思想,為大家介紹何謂響應式程式設計(Reactive Programming)。第二部分則結合兩個案例來講解如何在AKKA中實現響應式程式設計。第三部分則是這個主題的擴充套件,在介紹Reactive Manifesto的同時,介紹進行響應式程式設計更為主流的ReactiveX框架。
響應式程式設計(Reactive Programming)到底是什麼?從名詞定義來講,中文的響應式並沒有很好地展現Reactive的本意。響應這個詞語是一箇中性詞,本身沒有任何傾向。Reactive Programming強調的是“響應迅速”,響應使用者的請求要如電光火石一般迅捷,做到一觸即發。
傳統的順序程式設計採用每條指令依次執行的方式,倘若上一條指令沒有執行結束,當前的執行緒就得等著,任你如何提升機器效能還是程式碼效能,如果本質不變,始終改變不了響應需要等待的現實。若要響應迅速,就得把順序執行指令的方式換一換——同步換成非同步,方法執行換做訊息傳送,於是乎,我們可以精簡地定義:
響應式程式設計就是非同步資料流程式設計。
這其實是一種程式設計正規化,是程式設計理念的一種思想轉型。因為採用響應式程式設計,我們就不再將軟體要處理的業務視為物件,又或者函式,而是直接透析到本質:資料流(Data Stream)。
一言以蔽之:萬事萬物皆為流。
我這麼說,可能有些絕對。響應式程式設計並非銀彈,也非你手中四處尋找釘子來敲打的錘子。我們須得結合著實際的場景,考慮是否選擇響應式程式設計這種正規化。然而,如果我們侷限在響應式程式設計的語境下,我們確乎可以視萬事萬物為流。
從函數語言程式設計的角度來講,一連串組合函式的呼叫,其實就是資料在流動。函式可以抽象地視為一種資料型別到另一種資料型別的轉換。將各種形式的轉換(map、flatMap、filter)穿起來,同時保證資料的不變性(Immutable),則資料就能非常可靠地在函式鏈條中流動,或者被分析,或者被轉換,或者被過濾。
回到業務世界。我們幾乎可以將所有業務處理流程都可以建模為資料流的形式。這種流動差不多可以歸納為:
Command -> CommandHandler -> Event -> EventHandler -> Command ...
這是一種頗有節奏感的“建模儀式”。按照CQRS的設計思想,任何業務都可以分解為兩種形式的訊息:Query與Command。Query模型相對簡單,因為它本質上就是一個沒有副作用的只讀操作。執行Command本身是要改變業務物件值的,然而,如果我們將每次變更都視為是一種“狀態的遷移”,然後利用事件去記錄每次變更,就可以將可變轉換為不變。如果熟悉git,就會發現git的版本管理與此建模思想不謀而合。無論是新增、修改還是刪除程式碼,都可以視為是一次全新的提交。
當我們將程式設計的正規化切換為“流(Stream)”時,我們欣喜地發現,這種方式可以在很大程度上確保資料是不變的。這就為並行開發創造了可能。
然而,普通的資料流程式設計正規化並不能滿足“響應式Reactive”的本初定義。我們需要響應迅速。如何才能做到?那就是要做到沒有阻塞,這就是我們通常所說的非同步工作方式。
因而,響應式程式設計的設計原則是:
- 保持資料的不變性
- 沒有共享
- 阻塞是有害的
這或許也可以視為是響應式程式設計的特徵,恰好,這三條特徵也是Actor模型擁有的。
那麼,什麼是Actor模型?溯源Actor模型,它由Carl Hewitt在1973年提出,之後在Erlang OTP(Open Telecom Platform)中被廣泛應用在併發程式設計上。最初的Scala語言也實現了簡單的Actor模型,但隨著AKKA框架的推出,Scala放棄了自身的Actor,轉而選擇使用AKKA。
在《Scala併發程式設計》一書中,Aleksandar Prokopec形象地描述了Actor系統:
Actor系統模仿了人類的組織,如公司、政府和其他大型機構。在軟體公司中,有許多需要以併發方式達成的目標。為了實現這些目標,數百或數千名員工一起努力工作,而且這些員工通常會被組織成一種層次結構。許多員工會為級別比他們低的員工分派工作。為了高效地工作和決策,員工們使用電子郵件進行通訊。 當員工早上上班時,就會檢查他的電子郵箱並對重要的訊息做出迴應。如果某封電子郵件非常重要,那麼這個員工就必須立刻回覆這封郵件。當員工忙著回覆一封電子郵件時,可能會收到另一封電子郵件,而且後續的電子郵件都會進入他的電子郵箱中。只有當員工處理完成當前的電子郵件後,他才能繼續處理下一封電子郵件。
在這個隱喻中,軟體公司就相當於是一個ActorSystem,每位員工則是一個一個Actor。電子郵件是Actor之間彼此傳送的訊息(Message),一旦傳送了訊息,就不必等待收件人的回覆,可以繼續自己的工作,也就是說這種訊息傳送的方式是非同步非阻塞的。Actor持有的MailBox正好借用了這裡所謂的電子郵箱概念。
因而對於每個Actor而言:
- 每個Actor都擁有獨立的MailBox;
- 接收到的訊息皆為不可變物件,且完全獨立;
- 不管是tell訊息還是ask訊息,Actor執行訊息的方式都是非同步非阻塞的。
金風玉露一相逢,從某種意義上講,Actor模型就是響應式程式設計苦苦追尋的良緣佳配。二者天生匹配,且Actor模型的分散式特性還能更好地加強響應式程式設計的響應與處理速度。