響應式編程實現技術概述

響應式編程就是利用異步數據流進行編程,本質上就是觀察者(Observer)模式的一種表現形式。我們首先討論實現異步操作的幾種常見方式,然後引出響應式編程的主流實現技術。

1. 實現異步的常見方式

在Java中,爲了實現異步非阻塞,一般會採用回調(Callback)和Future這兩種機制,但這兩種機制都存在一定侷限性。

(1)回調

回調的含義如下圖所示,即類A的methodA()方法調用類B的methodB()方法,然後類B的methodB ()方法執行完畢後再主動調用類A的callback()方法。回調體現的是一種雙向的調用方式。

可以看到回調在任務執行過程中不會造成任何的阻塞,任務結果一旦就緒,回調就會被執行。但是我們也應該看到在使用回調機制時,代碼會從一個類中的某個方法跳到另一個類中的某個方法,從而造成流程的不連續性。對於單層的異步執行而言,回調很容易使用。但是對於嵌套的多層異步組合而言就顯得非常笨拙。所以回調很難大規模的組合起來使用,因爲很快就會導致代碼難以理解和維護,即形成所謂的“回調地獄(Callback Hell)”問題。

(2)Future

可以把Future模式簡單理解爲這樣一種場景:我們有一個希望處理的任務,然後把這個任務提交到Future,Future就會在一定時間內完成這個任務,而在這段時間內我們可以去做其它事情。作爲Future模式的實現,Java中的Future接口只包含如下5個方法。

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)?

    throws InterruptedException, ExecutionException, TimeoutException;

}

 

Future接口中的cancel()方法用於取消任務的執行;isCancelled()方法用於判斷任務是否已經取消;兩個get()方法會等待任務執行結束並獲取結果,區別在於是否可以設置超時時間;最後isDone()方法判斷任務是否已經完成。

Future雖然可以實現獲取異步執行結果的需求,但是它沒有提供通知機制,我們無法得知Future什麼時候完成。爲了獲取結果,我們要麼使用阻塞的兩種get()方法等待Future結果的返回,這時相當於執行同步操作;要麼使用isDone()方法輪詢地判斷Future是否完成,這樣會耗費CPU資源。所以,Future適合單層的簡單調用,對於嵌套的異步調用而言同樣非常笨重,不適合複雜的服務鏈路構建。

鑑於Future機制存在的缺陷,Java 8中引入了CompletableFuture機制。CompletableFuture一定程度上彌補了普通Future的缺點。在異步任務完成後,我們使用任務結果時則不需要等待。可以直接通過thenAccept()、thenApply()、thenCompose()等方法將前面異步處理的結果交給另外一個異步事件處理線程來處理。

CompletableFuture提供了非常強大的Future擴展功能,可以幫助我們簡化異步編程的複雜性,並且提供了函數式編程的能力,可以通過回調的方式處理計算結果,也提供了轉換和組合CompletableFuture所提供的各種方法。

對於日常的開發工作而言,大多數時候我們是在處理簡單的任務,這個時候使用CompletableFuture確實可以滿足需求。但是,當系統越來越複雜,或者我們需要處理的本身就是一個非常複雜的任務時, CompletableFuture對於多個處理過程的組合仍然不夠便捷。使用CompletableFuture編排多個Future是可行的,但並不容易。我們會擔心寫出來的代碼是否真的沒有問題,而隨着時間的推移,這些代碼會變得越來越複雜和難以維護。爲此,我們需要引入響應式編程的相關技術和框架,這些技術和框架能夠支持在未來更輕鬆地維護異步處理代碼。

2. 響應式編程的主流實現技術

目前,響應式編程的主流實現技術包括RxJava、Akka Streams、Vert.x和Project Reactor等。

(1)RxJava

Reactive Extensions(Rx)是一個類庫,它集成了異步、基於可觀察(Observable)序列的事件驅動編程,最早應用於微軟的.NET平臺。而RxJava是Reactive Extensions的Java實現,用於通過使用Observable/Flowable序列來構建異步和基於事件的程序庫,目前有1.x版本和2.x版本兩套實現。

RxJava 1.x版本誕生於響應式流規範之前,雖然可以和響應式流的接口進行轉換,但是由於底層實現的原因,使用起來並不是很直觀。RxJava 2在設計和實現時考慮到了與現有規範的整合,按照響應式流規範對接口進行了重寫,並把1.x版本中的背壓功能單獨分離出來。但爲了保持與RxJava 1.x版本的兼容性,RxJava 2在很多地方的使用也並不直觀。關於RxJava的更多內容可參考官網(http://reactivex.io/)。

(2)Akka Streams

Akka運行在JVM上,是構建高併發、分佈式和高彈性的消息驅動應用程序的一個工具套件。Actor是Akka中最核心的概念,它是一個封裝了狀態和行爲的對象,Actor之間可以通過交換消息的方式進行通信。通過Actor能夠簡化鎖及線程管理,可以非常容易地開發出正確的併發程序和並行系統。

Akka也是響應式流規範的初始成員,而Akka Streams是以Akka爲基礎的響應式流的實現,在Akka現有的角色模型之上提供了一種更高層級的抽象,支持背壓等響應式機制。

(3)Vert.x

Vert.x是Eclipse基金會下的一個開源的Java工具,是一個異步網絡應用開發框架,用來構建高併發、異步、可伸縮、多語言支持的Web應用程序。Vert.x就是爲了構建響應式系統而設計,基於事件驅動架構,Vert.x實現了非阻塞的任務處理機制。

Vert.x中包含Vert.x Reactive Streams工具庫,該工具庫提供了Vert.x上響應式流規範的實現。我們可以通過Vert.x提供的可讀流和可寫流處理響應式流規範中的發佈者和訂閱者。

(4)Project Reactor

Spring 5中引入了響應式編程機制,而Spring 5中默認集成了Project Reactor作爲該機制的實現框架。Reactor誕生較晚,可以認爲是第二代響應式開發框架。所以它是一款完全基於響應式流規範設計和實現的工具庫,沒有RxJava那樣的歷史包袱,在使用上更加的直觀易懂。但從設計理念和API的表現形式上,Reactor與RxJava比較類似,可以說Reactor基於響應式流規範,但在API方面又儘可能向RxJava靠攏。

Flux和Mono是Reactor中的兩個核心組件,Flux代表包含0到n個元素的異步序列,而Mono則表示包含0個或1個元素的異步序列。

 

如果對文章感興趣,可以關注我的微信公衆號:程序員向架構師轉型。

我出版了《系統架構設計:程序員向架構師轉型之路》、《向技術管理者轉型:軟件開發人員跨越行業、技術、管理的轉型思維與實踐》、《微服務設計原理與架構》、《微服務架構實戰》等書籍,並翻譯有《深入RabbitMQ》和《Spring5響應式編程實戰》,歡迎交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章