回應式編程
命令式編程(Imperative Programing),是一種描述計算機所需做出的行為的編程范式,詳細的命令機器怎么(How)去處理以達到想要的結果(What),
宣告式編程(Declarative Programing),是一種編程范式,與命令式編程相對立,它描述目標的性質,讓計算機明白目標,而非流程,只告訴機器想要的結果(What),機器自己摸索程序(How),
回應式編程(Reactive Programing)是一種關注資料流(data streams)和變化傳遞(propagation of charge)的異步編程方式,它屬于宣告式編程范式,
上面的文字理論性比較強,說的直白一點:
資料流就像一條河:它可以被觀測,被過濾,被操作(和 Java Stream 一致),或者為新的消費者與另外一條流合并為一條新的流,
回應式編程的一個關鍵概念是事件,事件可以被等待,可以觸發程序,也可以除法其它事件,事件是唯一以合適的方式將我們的現實世界映射到我們的軟體中:如果屋里太熱了,我們就打開一扇窗,
同樣的,當我們的天氣 app 從服務器端獲取到新的天氣資料后,我們需要更新 app 上展示天氣的 UI ;汽車上的車道偏移系統探測到車輛偏移了正常路線就會提醒駕駛者糾正;這些就是回應事件,
回應式編程可以理解為面向物件中觀察者模式(Observer Design Pattern)的一種擴展,它也與迭代器模式(Iterator Design Pattern)
有相同之處,回應式流(reactive streams)其中也有 Iterable - Iterator 這樣的對應關系,主要的區別在于 Iterator 是基于拉取(pull)
方式的,而回應式流是基于推送(push)方式的,
Iterator 迭代器(模式)也是命令式編程方式,即使訪問值的方法使用的是 iterable 方法,什么時候獲取 next 是由開發者決定的,在回應式流中,相對應的角色是發布者(publisher) - 訂閱者(Subscriber),當有新值到來的時候,是由發布者通知訂閱者,這種典型的推送方式是回應式流的關鍵,同時對推送來的資料的操作是宣告式的表達,而不是命令式的,開發者通過描述控制流程來定義對資料流的處理邏輯,
除了資料推送,回應式流還提供了資料流完成的信號和發生錯誤的信號,一個發布者可以隨時向訂閱者推送資料(onNext),同時也可以推送錯誤(onError)和完成信號(onComplete),錯誤和完成信號都將終止回應流,
說了這么多理論性描述,那么回應式編程到底牛叉在哪里呢?
阻塞是資源浪費
以現實中的雙11秒殺為例,當大量用戶同時在0點發起搶購某個商品時,假設在不做任何限流、架構優化等的情況下,大量的請求將同時進入到后端,以tomcat容器為例,tomcat將為用戶創建大量的執行緒(受執行緒池控制)來回應用戶請求,后端的代碼收到請求后,需要執行如下邏輯:判斷用戶的下單請求是否合理有效,判斷用戶是否參與過當前商品的秒殺,判斷當前商品庫存是否充足,如果庫存充足,執行下單鎖庫存,隨著并發數的加大,這一套代碼邏輯執行下來將會花費很長時間,同時tomcat之前的執行緒一直阻塞著,等待servlet的結果來最終回應給用戶,
現代應用面對大量的并發用戶,即使硬體的處理能力高速發展,軟體性能仍是關鍵因素,
廣義來說,有兩種方式來提高軟體性能:
- 并行化,使用更多的執行緒和硬體資源;
- 優化現有(代碼)資源,提高執行效率,
通常,開發者使用阻塞式撰寫代碼,在出現性能瓶頸后,我們可以增加處理執行緒,執行緒中同樣是阻塞的代碼,但是這種使用資源的方式會面臨資源的競爭和并發問題,同時,阻塞會浪費資源,具體來說,當一個程式面臨延遲(通常是 I/O 方面,比如資料庫讀寫和網路請求),所在執行緒進入 idle 狀態等待回傳,從而浪費資源,所以并行化并非解決問題的最佳方式,它是挖掘硬體潛力的方式,但是帶來了復雜性,并造成了對資源的浪費,
異步可以解決問題嘛?
第 2 個思路,提高執行效率,通過撰寫異步非阻塞的代碼,任務發起異步呼叫后,執行程序會切換到另一個(使用同樣底層資源)活躍任務,等待異步任務回傳結果后再去處理,這樣可以解決資源的浪費問題,
Java 提供了2種方式實作異步代碼:
- 回呼:異步方法沒有回傳值,而是采用一個 callback 作為引數,當有結果后,回呼這個 callback,常見的例子,比如 Swing 中
的 EventListener, - Future:異步方法立即回傳一個 Future
,Future 的結果不是立馬可以拿到,需要等待異步任務執行完成后,才可以使用,
但是這兩個方法都有他們的局限性:
- 如果在回呼中嵌套回呼時,多層嵌套的回呼將導致代碼難以理解和維護,即所謂的嵌套地獄,
- Future 比回呼要好一點,即使在 Java 8 中引入了 CompletableFuture,他對于多個處理的組合仍不友好,編排多個 Future 是可行的,
但卻不易,此外,Future 還有一個問題,當對 Future 物件呼叫 get() 方法時,仍然會導致阻塞,并且缺乏對多個值以及更進一步對錯誤
的處理,
從命令式編程到回應式編程
基于上面提到的問題,開發牛人們提出了回應式流解決方案,在回應式編程方面,微軟是先行者,他們率先在 .NET 中創建了回應式擴展庫(Reactive Extensions Library, Rx),接著,RxJava 在 JVM 上實作了回應式編程,后來,在 JVM 平臺出現了一套回應式編程規范,它定義了一系列編程介面和互動規范,并整合到了 Java 9 中,
除了解決上述問題,回應式編程庫還額外關注如下幾個方面:
- 可編排性(Composability)和可讀性(Readability)
- 提供豐富的運算子(operators)來處理形如流的資料
- 在訂閱(Subscribe)之前什么都不發生
- 背壓(BackPressure)支持,簡單來說,訂閱者能夠反向告知發布者生產內容速度的能力
- 高層次的抽象,從而達到并發無關的效果
RxJava 和 Reactor
RxJava
RxJava 是 Reactive Extensions 的 Java VM 實作,它用于通過使用可觀察的序列來組成異步和基于事件的程式,
它擴展了觀察者模式以支持資料/事件序列,并添加了運算子,使您可以宣告性地將序列組合在一起,同時消除了對諸如多執行緒,同步,執行緒安全和并發資料結構之類的問題的擔憂,
RxJava 是 Java 界回應式編程的先行者,因為是先有的 RxJava,才后有的 Java 版本的回應式編程規范,同時該規范定義時參考了 RxJava 的許多定義,RxJava 的早期版本最低支持 Java 6,官方版本直至 Java 9 中才被支持,
RxJava 最新版本 3.x,最低需要 Java 8+,官方請看 https://github.com/ReactiveX/RxJava,
Reactor
Reactor 是一個用于 JVM 的完全非阻塞的回應式編程框架,具備高效的需求管理(即對 “背壓(backpressure)”的控制)能力,它與 Java 8 函式式 API 直接集成,比如 CompletableFuture, Stream, 以及 Duration,它提供了異步序列 API Flux(用于[N]個元素)和 Mono(用于 [0|1]個元素),并完全遵循和實作了“回應式擴展規范”(Reactive Extensions Specification),
Reactor 的 reactor-ipc 組件還支持非阻塞的行程間通信(inter-process communication, IPC), Reactor IPC 為 HTTP(包括 Websocket)、TCP 和 UDP 提供了支持背壓的網路引擎,從而適用于微服務架構,并且完整支持回應式編解碼(reactive encoding and decoding),
Reactor 是第四代回應式框架,跟 RxJava 2 有些相似,Reactor 專案由 Pivotal 啟動,以回應式流規范、Java8 和 ReactiveX 術語表為基礎,它的設計是 Reactor 2(上一個主要版本)和 RxJava 核心貢獻者共同努力的結果,
從設計概念方面來看,RxJava 有點類似 Java 8 Steams API,而 Reactor 看起來有點像 RxJava,不過這決不只是個巧合,這樣的設計是為了能夠給復雜的異步邏輯提供一套原生的具有 Rx 操作風格的回應式流 API,所以說 Reactor 扎根于回應式流,同時在 API 方面盡可能地與 RxJava 靠攏,
Reactor 最新版本3.x,最低需要 Java 8+,官方請看 https://github.com/reactor/reactor-core,
Java Stream Vs RxJava Vs Reactor
我們在前傳中首先學習了Java Stream,通過上面筆記的介紹,發現 Java Stream 在很多方面與回應式編程方面相似,那么他們到底有哪些區別呢,來看徐靖峰在【八個層面比較 Java 8, RxJava, Reactor】中的下面這張圖:

由于上述文章已經講解的很好了,請大家跳轉過去學習一下,
總結
RxJava 在 Android 開發界可算是如火如荼,通過事件的回應配合界面的操作可謂如魚得水,Reactor 直至 Spring 5 中引入了 Reactive Stream 及 Spring WebFlux 才進入了我的視線,RxJava 的學習內容基本遍地都是了,而 Reactor 的內容卻少之又少,這也是本片筆記的由來,
回應式編程的理論部分,總算是介紹完了,理論知識一直是我的弱勢,上面的大部分內容都是源自 Reactor 的官方檔案及下方各個參考檔案,感興趣的朋友可到對應的文章學習了解下,
今天的內容就講到這里,我們下篇開始 Reactor 的學習,
參考
- 回應式規范
- Reactor 3 中文指南
- 這可能是最好的RxJava 2.x 教程(完結版)
- 重新理解回應式編程
- 八個層面比較 Java 8, RxJava, Reactor
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/161505.html
標籤:Java
上一篇:到底什么級別才算是高并發?
下一篇:Java多執行緒
