很多開(kāi)發(fā)人員,不論是前端、后端還是移動(dòng)端都會(huì)從各種途徑中獲知響應(yīng)式編程這個(gè)概念,因?yàn)楹芏嗫蚣芏奸_(kāi)始宣稱(chēng)支持響應(yīng)式開(kāi)發(fā)。在我們意識(shí)到這一點(diǎn)時(shí),大家都會(huì)嘗試搜索各種文章資料想了解響應(yīng)式編程到底是什么,可是互聯(lián)網(wǎng)資料真的是太“豐富”了,豐富到你看了看了半天,可能還摸不清頭腦,因?yàn)轫憫?yīng)式理論是從問(wèn)題的底層重建了整個(gè)概念體系,一個(gè)概念牽扯出另一個(gè)概念,然后概念越來(lái)越多,很容易把人繞暈。本文嘗試在表面梳理一下響應(yīng)式編程的脈絡(luò),力求清晰,供各位想深入研究的有所參考。 要理解響應(yīng)式編程的脈絡(luò),就需要將這個(gè)詞拆分成兩部分:響應(yīng)式 和 編程。 編程 編程指的是編程范式,計(jì)算機(jī)語(yǔ)言發(fā)展到今天,出現(xiàn)了很多編程范式(詳見(jiàn)維基百科),響應(yīng)式編程是最近幾年流行起來(lái)的。除了響應(yīng)式編程之外,我們常用的編程范式還有命令式編程和在命令式編程基礎(chǔ)上擴(kuò)展的面向?qū)ο缶幊?/strong>以及函數(shù)式編程。
因此,總結(jié)一下,現(xiàn)在流行的編程范式:命令式、面向?qū)ο?、函?shù)式和響應(yīng)式。 響應(yīng)式 在命令式編程中,對(duì)于語(yǔ)句:a:=b+c,當(dāng)語(yǔ)句執(zhí)行時(shí),a獲得了語(yǔ)句的執(zhí)行結(jié)果,此后,參數(shù)b和c的任何改變對(duì)a沒(méi)有影響。在響應(yīng)式編程中,對(duì)于語(yǔ)句:a:=b+c,當(dāng)參數(shù)b和c發(fā)生變化時(shí),a會(huì)自動(dòng)隨著變化,而不必重新執(zhí)行語(yǔ)句。 一些直觀的示例可以更好的理解“響應(yīng)”,如:Excel表格中,某個(gè)單元格產(chǎn)生變化時(shí),其它單元格也會(huì)產(chǎn)生相應(yīng)的變化。APP從服務(wù)器接收到某條交易記錄時(shí)自動(dòng)顯示在UI界面上。 響應(yīng)式作為一種設(shè)計(jì)思想在不同層面不同維度可以有不同的應(yīng)用。應(yīng)用到平常意義的數(shù)據(jù)流層面,就是Reactive Stream;如果應(yīng)用到服務(wù)流層面,就是響應(yīng)式微服務(wù);用在Web設(shè)計(jì)上就有了響應(yīng)式Web設(shè)計(jì);用在架構(gòu)層面就是響應(yīng)式架構(gòu)。 響應(yīng)式編程 響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的異步編程范例,數(shù)據(jù)流可以是I/O,事件等。 通常,某種具體的編程語(yǔ)言都會(huì)傾向于特定的編程范式,如Fortran語(yǔ)言是命令式編程語(yǔ)言,Java在設(shè)計(jì)上更加支持面向?qū)ο缶幊?,Haskell是函數(shù)式編程語(yǔ)言,而Scala則同時(shí)支持函數(shù)式和面向?qū)ο缶幊獭m憫?yīng)式編程沒(méi)有特定的編程語(yǔ)言,其實(shí)現(xiàn)是建立在現(xiàn)有編程語(yǔ)言基礎(chǔ)之上的,因此就有:
具體實(shí)現(xiàn) 針對(duì)響應(yīng)式編程理論,當(dāng)前主流實(shí)現(xiàn)有兩類(lèi):一種是函數(shù)響應(yīng)式編程FPR,一種是具有函數(shù)風(fēng)格的響應(yīng)式實(shí)現(xiàn)——響應(yīng)式函數(shù)編程RFP(reactive-functional programming)。 注意,這是兩種不同的實(shí)現(xiàn),函數(shù)響應(yīng)式編程FRP是建立在函數(shù)編程之上的響應(yīng)式實(shí)現(xiàn);響應(yīng)式函數(shù)編程RFP是在命令式操作系統(tǒng)之上的一層抽象,借鑒了函數(shù)式編程風(fēng)格,用于處理異步和事件驅(qū)動(dòng)場(chǎng)景,同時(shí)可以避免像計(jì)算機(jī)需要跨線程和網(wǎng)絡(luò)邊界處理復(fù)雜的狀態(tài)那樣思考問(wèn)題。 函數(shù)響應(yīng)式編程FPR有基于Scala語(yǔ)言實(shí)現(xiàn)的Akka-Streams,響應(yīng)式函數(shù)編程RFP有ReactiveX和Reactor等。 ReactiveX ReactiveX,即Reactive Extension,響應(yīng)式擴(kuò)展Rx。 Eric于2007年夏天在Microsoft創(chuàng)建了Rx,2009年11月18日Rx.Net發(fā)布,并移植到Microsoft.Phone.Reactive for Windows Phone 7。Jafar Husain于2011年離開(kāi)Microsoft加入Netflix,開(kāi)始布道Rx并在Netflix的客戶(hù)端UI棧完整地實(shí)現(xiàn)了異步流式處理。然后,他開(kāi)始推動(dòng)負(fù)責(zé)Netflix中間層API的Ben Christensen使用Rx。Ben于2012年開(kāi)始RxJava并在2013年將代碼遷移到Github。 隨著Rx在業(yè)界越來(lái)越受歡迎,Eric于2012年離開(kāi)Microsoft并創(chuàng)建了Applied Duality公司,將所有精力投入到構(gòu)建標(biāo)準(zhǔn)化的跨語(yǔ)言,跨平臺(tái)的異步實(shí)時(shí)數(shù)據(jù)流式處理API,即ReactiveX.io。 當(dāng)前,Rx包括RxJava,RxJS,Rx.Net,RxScala,RxClojure,RxSwift等18種語(yǔ)言實(shí)現(xiàn)。 Reactor Reactor項(xiàng)目是Spring基于Reactive Streams規(guī)范(由4個(gè)簡(jiǎn)單的Java接口:Publisher,Subscriber,Subscription 和 Processor,一個(gè)文本規(guī)范和一個(gè)TCK組成)提供的響應(yīng)式實(shí)現(xiàn),目的是為了讓Spring生態(tài)體系能夠支持業(yè)界越來(lái)越火的響應(yīng)式開(kāi)發(fā)。 Spring并未直接引入類(lèi)似RxJava來(lái)作為其框架的一部分,而是選擇另造輪子。從Spring 5開(kāi)始,開(kāi)發(fā)人員就可以有Spring Web Reactive和Spring Web MVC兩種選擇。 當(dāng)前,Reactor項(xiàng)目包含Reactor Core,Reactor Test,Reactor Adapter,Reactor Netty,Reactor Extra,Reactor Kafka,Reactor Core .NET 和 Reactor Core JS。 Rx的特點(diǎn) 本節(jié)摘自《ReactiveX/RxJava文檔中文版》 Rx從同步集合的Iterable/Iterator反向演化而來(lái),是一個(gè)使用可觀察數(shù)據(jù)流進(jìn)行異步編程的編程接口,ReactiveX結(jié)合了觀察者模式、迭代器模式和函數(shù)式編程的精華。 使用觀察者模式
簡(jiǎn)化代碼
使用Observable的優(yōu)勢(shì) Rx擴(kuò)展了觀察者模式用于支持?jǐn)?shù)據(jù)和事件序列,添加了一些操作符,它讓你可以聲明式的組合這些序列,而無(wú)需關(guān)注底層的實(shí)現(xiàn):如線程、同步、線程安全、并發(fā)數(shù)據(jù)結(jié)構(gòu)和非阻塞IO。 在Rx中,一個(gè)觀察者(Observer)訂閱一個(gè)可觀察對(duì)象(Observable)。觀察者對(duì)Observable推送的數(shù)據(jù)或數(shù)據(jù)序列作出響應(yīng)。這種模式可以極大地簡(jiǎn)化并發(fā)操作,因?yàn)樗鼊?chuàng)建了一個(gè)處于待命狀態(tài)的觀察者哨兵,在未來(lái)某個(gè)時(shí)刻響應(yīng)Observable的通知,不需要阻塞等待Observable推送數(shù)據(jù)。 概念與術(shù)語(yǔ)
直譯為反應(yīng)性的,有活性的,根據(jù)上下文一般翻譯為反應(yīng)式、響應(yīng)式
可迭代對(duì)象,支持以迭代器的形式遍歷,許多語(yǔ)言中都存在這個(gè)概念
可觀察對(duì)象,在Rx中定義為更強(qiáng)大的Iterable,在觀察者模式中是被觀察的對(duì)象,一旦數(shù)據(jù)產(chǎn)生或發(fā)生變化,會(huì)通過(guò)某種方式通知觀察者或訂閱者
觀察者對(duì)象,監(jiān)聽(tīng)Observable推送的數(shù)據(jù)并做出響應(yīng),Subscriber是它的一個(gè)特殊實(shí)現(xiàn)
(摘自Scala官方文檔) 所謂Future,是一種用于指代某個(gè)尚未就緒的值的對(duì)象。而這個(gè)值,往往是某個(gè)計(jì)算過(guò)程的結(jié)果:若該計(jì)算過(guò)程尚未完成,我們就說(shuō)該Future未就位;若該計(jì)算過(guò)程正常結(jié)束,或中途拋出異常,我們就說(shuō)該Future已就位。 Future的就位分為兩種情況:當(dāng)Future帶著某個(gè)值就位時(shí),我們就說(shuō)該Future攜帶計(jì)算結(jié)果成功就位。當(dāng)Future因?qū)?yīng)計(jì)算過(guò)程拋出異常而就緒,我們就說(shuō)這個(gè)Future因該異常而失敗。 Future的一個(gè)重要屬性在于它只能被賦值一次。一旦給定了某個(gè)值或某個(gè)異常,future對(duì)象就變成了不可變對(duì)象——無(wú)法再被改寫(xiě)。
(摘自Scala官方文檔) 如果說(shuō)futures是為了一個(gè)還沒(méi)有存在的結(jié)果,而當(dāng)成一種只讀占位符的對(duì)象類(lèi)型去創(chuàng)建,那么promise就被認(rèn)為是一個(gè)可寫(xiě)的,可以實(shí)現(xiàn)一個(gè)future的單一賦值容器。這就是說(shuō),promise通過(guò)這種success方法可以成功去實(shí)現(xiàn)一個(gè)帶有值的future。相反的,因?yàn)橐粋€(gè)失敗的promise通過(guò)failure方法就會(huì)實(shí)現(xiàn)一個(gè)帶有異常的future。
當(dāng)我們使用迭代遍歷一個(gè)集合時(shí),我們是從集合中拉?。≒ull)數(shù)據(jù)的。當(dāng)我們?cè)诩匣驍?shù)據(jù)流上使用觀察者模式時(shí),數(shù)據(jù)是推送給觀察者的。
在Rx中,可觀察對(duì)象Observable同時(shí)支持同步和異步實(shí)現(xiàn)。
在Rx中,可觀察對(duì)象Observable是有惰性的,只有被訂閱時(shí),它才推送變化。這不同于eager類(lèi)型如Future。
在Rx中,一個(gè)可觀察對(duì)象Observable流既不支持并發(fā)也不支持并行。不過(guò),在一組異步的可觀察對(duì)象Observable流集合可以執(zhí)行并發(fā)和并行處理操作。 結(jié)論 本文粗略梳理了響應(yīng)式編程的概念體系,深入研究和實(shí)踐還需要沿著這條脈絡(luò)進(jìn)行深層次的挖掘。不當(dāng)之處請(qǐng)回復(fù)指正。 |
|