使用Java 9 Flow進行響應式程式設計

banq發表於2018-11-28

在本指南中,您將學習Java 9中的Flow API如何幫助您使用新的Publisher和Subscriber構建反應模式。閱讀之後,您應該能夠理解這種新的程式設計風格及其優缺點。
本指南重點介紹新的  Flow API,它使我們能夠僅使用JDK採用Reactive Programming,而不需要其他庫,如RxJava或Project Reactor等。

但是,在看了API之後,你很快就會發現這個API由幾個介面和一個實現組成:
  • 介面  Flow.Publisher <T>定義了生成專案和控制訊號的方法。
  • Flow.Subscriber <T> 介面  定義了接收這些訊息和訊號的方法。
  • Flow.Subscription介面   定義了連結釋出伺服器和訂閱伺服器的方法。
  • 介面  Flow.Processor <T,R>定義了一些方法來執行一些高階操作,例如將專案的轉換從釋出者連結到訂閱者。
  • 最後,SubmissionPublisher類  <T>實現了Flow.Publisher <T>,它是一個靈活的專案生成器,符合Reactive Streams計劃。

即使沒有很多類可以使用,包括Java 9中的這個API也是一個重大變化:第三方可以為依賴於這些介面的庫提供Reactive支援,例如從JDBC驅動程式到RabbitMQ的反應式實現。

從Pull 到Push 再到Pull-Push
反應式程式設計主要是消費者控制資料流,由於它整合在主要框架和庫分發版(例如Java 9或Spring 5)中,它現在變得流行,並且分散式系統的興起帶來了大量的資料需要相互關聯傳達。
回顧過去有助於我們瞭解其崛起。幾年前,從消費者那裡獲取資料的最流行的技術是基於Pull 拉的機制。客戶端定期輪詢資料,如果可用,他們會讀取資料。優點是,在資源較少的情況下,他們可以控制資料流(停止輪詢); 主要缺點是在沒有任何消耗時透過輪詢資料浪費處理時間和/或網路資源。
隨著時間的推移,趨勢發生了變化,從生產者那裡推送資料並讓消費者照顧它變得很受歡迎。問題在於消費者可能擁有比生產者更有限的資源,在消費者緩慢和資料丟失的情況下最終會得到完整的輸入緩衝。如果只發生在我們訂閱者中的一小部分使用者,這可能會很好,但是如果它發生在大多數使用者身上呢?我們可以做得更好,減緩我們的生產者釋出...
Reactive Programming附帶的混合推拉Pull-Push方法試圖帶來兩全其美:它讓消費者負責請求資料並控制來自發布者的流量,這也可以在阻止或丟棄資料時做出決定資源。我們將在下面看到一個很好的實際例子。

Flow和Stream的區別
應式程式設計並不是取代函式程式設計。兩者相容並且完美地協同工作。雖然Java 8中引入的Streams API非常適合處理資料流(map,reduce和所有變體),但Flow API會在通訊方面(請求,減速,丟棄,阻塞等)發揮作用。
您可以將Streams用作Publisher的資料來源,根據需要阻止它們或刪除專案。您也可以在訂閱者身邊使用它們,例如,在收到某些專案後執行聚合。更不用說所有其他的程式設計邏輯,其中反應流不適合,但它可以用函式式編寫,並且比指令式程式設計的可讀性和維護更容易十倍。
有個困擾:如果你需要兩個系統之間交換並轉換資料怎麼辦?Stream和Flow如何一起工作?在這種情況下,我們可以使用Java 8函式將源對映到目標(轉換它)但我們不能在釋出者和訂閱者之間使用Stream,對嗎?
我們可能會想到在兩者之間建立一個訂閱者,它從原始釋出者那裡獲取專案,轉換它,然後像釋出者那樣釋出。好訊息:這就是Java 9的Flow.Processor<T, R>模式, 所以我們只需要實現該介面並在那裡編寫函式來轉換資料。
就個人而言,我不喜歡全反應,  過度反應或成為反應性佈道者(我無法決定具體的術語)。儘量不要為此瘋狂。

案例
本指南中包含的示例程式碼模擬了Magazine Publisher用例。釋出者只有兩個訂閱者。

出版商將為每位訂閱者製作一系列20種雜誌。他們知道他們的讀者在交貨時通常不在家,他們想避免郵遞員退回雜誌或扔掉雜誌。這可能發生,因為釋出者知道訂閱者的郵箱通常很小,無法放置更多郵件(訂閱者的緩衝區)。
取而代之的是,他們實施了一個非常創新的交付系統:使用者在他們在家時打電話給他們,他們幾乎立即交付一本雜誌。出版商計劃在辦公室為每位訂閱者保留一個小盒子,以防有些人在釋出雜誌後不立即致電該雜誌。經理認為在發行人辦公室為每個訂戶保留最多8個雜誌的空間已經足夠了(注意緩衝區現在是如何在出版商那邊)。
然後,其中一名工人前往經理辦公室,警告他們不同的情況:

  • 如果訂閱者的速度足以要求傳送,就像釋出者正在列印新雜誌一樣快,就不會有空間問題。
  • 如果訂閱者沒有像列印雜誌那樣以相同的速度打電話,那麼這些盒子可能會變滿。工人要求管理者應如何  作出反應,以這種情況下:
    1. 將每個使用者的盒子大小增加到20,這將解決問題(即釋出者方面的更多資源)。
    2. 停止列印直到情況得到修復(訂閱者請求至少一個)然後放慢速度,這會損害一些可能足夠快以使他們的盒子保持空白的訂閱者。
    3. 在生產後立即將任何不適合訂戶盒的雜誌扔到回收站(丟棄)。
    4. 中間解決方案:如果任何一個框已滿,請在列印下一個數字之前等待最長時間。如果在那之後還沒有空間,那麼他們將回收(丟棄)新號碼。

經理解釋說,他們買不起第一個解決方案,花費這麼多資源只是為了應對緩慢的使用者將是一種浪費,並決定選擇禮貌等待(d),這可能會損害一些使用者,但只是為了減少時間。營銷團隊決定將這種方法稱為Reactive Magazine Publishing,因為它“適應了他們的讀者”。提升上述分析的工人成為本月的員工。

(暈倒,這個案例用例夠複雜的,業務複雜,技術複雜,不適合學習!需要詳細瞭解可點選標題看原文)

 在GitHub上找到原始碼:Java 9 Flow - Reactive

相關文章