您好,登錄后才能下訂單哦!
在前一篇文章從Reactive編程到“好萊塢”中,談到了響應式的一些概念,講的有些發散。 但僅僅還是停留在概念的層面,對于實戰性的東西并沒有涉及。
所以大家看了后,或許還是有些不痛不癢。
響應式編程強調的是異步化、面向流的處理方式,這兩者也并非憑空生出,而是從大量的技術實踐中總結提煉出來的概念,就比如:
我們談異步化,容易聯想到 Java 異步IO(Asynchronized IO),而且習慣于將其和 BIO、NIO等概念來做對比。 殊不知,老早出現的 Swing 框架(Java UI)就已經將異步化思維玩的很溜了,不信的可以看看其內部 Observer模式(觀察者)的實現。
除了前端,Reactive 概念在大數據領域的應用其實非常的廣泛了。 但是對于大多數做 Web 后端開發的人來說或許普及程度并不高,以筆者自身的感受是,碼了這么些年頭,除了做好代碼分層之外,似乎也沒有見到 Reactive可以發揮重大作用的地方。 原因就在于,在Web 后端開發領域基本是依托 HTTP協議機制實現的,這是一個相當簡單的 請求 -> 應答 交互模式,客戶端在發送請求后,會一直等待結果返回,也就是結果的通知是由客戶端主動獲取而非異步通知的,因此并不是 Reactive 的風格。 但這已經是符合用戶一貫的使用方式了,絕大多數情況下并不需要做什么樣的變化,此時我們對響應式的感知并不深刻。
更符合Reactive 的另外一個場景是 富客戶端(Rich Application),假設在需要大量復雜的前端交互的場景下,我們可以選擇將一些邏輯放在前端代碼中實現。
此時的 Web 交互就不再是整個頁面的刷新,而是演變為客戶端與服務端的"實時"雙向通訊,這類應用也比較普遍了,比如基于 WebSocket 實現的 在線通信、互動應用 等等。
淺顯的從趨勢上看, Reactive 的前景還是很明朗的,這里并不是說因為現在多數流行的編程語言中都有它的影子(比如提供了Rx風格的框架)。
而是未來的大數據處理、實時流計算會成為主流,這是環境決定的。 而這時 Reactive 這種"面向流"的編程模式無疑是很合適的。
Java 平臺直到 JDK 9 才提供了對于 Reactive 的完整支持,而在此之前的JDK版本中,也以及存在一些有關聯性的API,比如:
這些關聯性API 并不是完整的 Reactive,Java 9所支持的 Reactive Stream API 來自于2013年的響應式流規范(Reactive Stream Specification)。
https://www.reactive-streams.org/
基于這個規范中主要定義了下面幾個接口:
Java的響應式流接口統一定義在 java.util.concurrent.Flow接口
Publisher
即數據的發布者。 Publisher 接口定義了一個subscribe方法,用于添加訂閱者:
首先,在subscribe方法調用成功后,Subscriber的 onSubscribe(Subscription s) 方法會被觸發(Subscription 表示當前的訂閱關系)。
此后,正常可以繼續調用 Subscription 的 request(long n) 方法來向發布者請求數據,n是指最大的數據條目數。
發布者會產生3種不同的消息,分別對應到 Subscriber 的3個回調方法:
數據消息:對應 onNext 方法,表示發布者產生的數據。
錯誤消息:對應 onError 方法,表示發布者產生了錯誤。
結束消息:對應 onComplete 方法,表示發布者已經完成了所有數據的發布。
在上面的3種通知中,錯誤、結束消息都表示當前的流已經到達了終點,后面不再會有消息產生。
Subscription
Subscription 表示的是一個訂閱關系。 可以通過該對象請求數據(request方法),或者取消訂閱(cancel方法)。
負壓的支持
負壓是響應式流定義的一種重要的能力,在上述的接口中,實質上已經提供了負壓的支持。
Publisher 只有在收到請求之后,才會產生數據。 這就保證了 Subscriber 可以根據自己的處理能力,確定要向 Publisher 請求的數據量,以此保證自身不會被沖垮。
下面,以一個簡單的代碼示例來演示 Reactive Stream API 是如何使用的。
以制奶廠為例,為了提高營收,工廠推出了一個廠家直銷的業務。 顧客可以直接向廠方訂購一定天數的奶制品,每天則是由工廠的服務人員送貨上門。
為了模擬這個場景,我們實現的代碼如下:
public class MilkFactory extends SubmissionPublisher<String> {
private final ScheduledFuture<?> periodicTask;
private final ScheduledExecutorService scheduler;
private static final List<String> milks = Arrays.asList("益力多", "酸牛奶", "原味奶", "低脂蛋奶", "羊奶", "甜牛奶");
public MilkFactory() {
super();
//初始化定時器
scheduler = new ScheduledThreadPoolExecutor(1);
//每一天生產完牛奶并推送給消費者
periodicTask = scheduler.scheduleAtFixedRate(
() -> submit(produceMilk()), 0, 1, TimeUnit.SECONDS);
}
//隨機生產牛奶
private String produceMilk() {
return milks.get((int) (Math.random() * milks.size()));
}
//關閉流
public void close() {
periodicTask.cancel(false);
scheduler.shutdown();
super.close();
}
}
MilkFactory 集成自SubmissionPublisher(一個提供緩沖的Publisher實現),其內部會啟動一個定時器,用于模擬每天給用戶發放生產的牛奶。
通過submit()方法可以將數據推送給用戶。
public class MilkCustomer implements Flow.Subscriber<String> {
private Flow.Subscription subscription;
private AtomicInteger available = new AtomicInteger(0);
private int dayCount;
public MilkCustomer(int dayCount) {
this.dayCount = dayCount;
}
@Override
public void onSubscribe(Flow.Subscription subscription) {
this.subscription = subscription;
//設置總量
available.set(dayCount);
//第一天
subscription.request(1);
}
@Override
public void onNext(String milk) {
System.out.println("今天的牛奶到了: " + milk);
//如果還有存量,繼續請求
if(available.decrementAndGet() > 0){
subscription.request(1);
}else{
System.out.println("牛奶套餐已經派完,歡迎繼續訂購");
this.subscription.cancel();
}
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("closed.");
}
}
MilkCustomer 接受一個dayCount入參,即表示訂購的數量,在首次訂閱時會請求第一天的奶品,此后則每次收到到奶品后再請求下一天的,直到將總量消費完。
執行下面的代碼:
MilkFactory factory = new MilkFactory();
//訂閱1周
MilkCustomer customer = new MilkCustomer(7);
factory.subscribe(customer);
輸出:
今天的牛奶到了: 酸牛奶
今天的牛奶到了: 羊奶
今天的牛奶到了: 原味奶
牛奶套餐已經派完,歡迎繼續訂購
在上例中,我們使用 Java 提供的 Reactive Stream API 實現了一個"在線送奶" 的業務流。
整個過程相對是比較簡單的,最關鍵的地方就在于對流式處理以及訂閱關系的理解。 然而目前的 Reactive 實現還沒有完全的統一,比如 Spring WebFlux(SpringBoot 2支持) 仍然是基于 Reactor 私有API而不是 Reactive Stream API 來構建的,后面有機會再做下介紹。
關于Future和CompletableFuture的區別
https://juejin.im/post/5adbf8226fb9a07aac240a67
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。