Spring5的webflux可以支持高吞吐量,使用相同的資源可以處理更加多的請求,它將會成為未來技術的趨勢,但是相對于學習其他的框架相比,它的學習曲線很高,綜合了很多現有的技術,即使按照教程學習能編寫代碼,但是仍然會感覺不夠理解,要想真正的理解webflux,我覺得需要掌握以下幾點:
1.學習jdk8的lambda表達式和stream流編程思想,
2.理解響應式編程概念,理解背壓和實現機制。
理解了以上兩點,很容易理解webflux的基石reactor,再學習webflux就很簡單了!
學習lambda表達式
Lambda 表達式是一個匿名函數,源于數學λ演算。是閉包函數,但閉包并不一定是Lambda 函數。
它可以賦值給變量,作為函數參數,作為函數返回值。
Lambda的本質在編譯的時候在類中動態生成method方法,方法可以是static的也可以是非static的,主要取決于表達式內是否包含this這個變量,如果包含就生成動態的method方法,如果不包含就生成靜態的method方法。
Lambda表達式的優點,大大的降低了編寫代碼的量,并大大提高了代碼的可維護和可閱讀性。
Lambda的惰性求值,只有在實際調用過程中才觸發,像日志輸出:
// 打印日志前需要先判斷日志級別if(logger.isLoggable(Level.DEBUG)){logger.debug("====用戶:"+username);}
如果沒有if條件判斷,即使level不是debug,也會把執行“====用戶:”+username的操作,浪費了性能,如果加上if條件判斷代碼量又很大,不利于代碼的維護。如果用Lambda表達式是如何呢?
// 使用lambda表達式的惰性求值,不需要判斷日志級別logger.debug(()->"====用戶:"+username);
因為Lambda的特點,實際上字符串相加的操作并不會執行,同時代碼也少了,也容易維護了。
Stream流的概念
Stream 不是集合元素,它不是數據結構并不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素并對其執行某些操作;高級版本的 Stream,用戶只要給出需要對其包含的元素執行什么操作,比如 “過濾掉長度大于 10 的字符串”、“獲取每個字符串的首字母”等,Stream 會隱式地在內部進行遍歷,做出相應的數據轉換。
Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次后即用盡了,就好比流水從面前流過,一去不復返。
而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顧名思義,當使用串行方式去遍歷時,每個 item 讀完后再讀下一個 item。而使用并行去遍歷時,數據會被分成多個段,其中每一個都在不同的線程中處理,然后將結果一起輸出。Stream 的并行操作依賴于 Java7 中引入的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。
這個特點大大提高了硬件設備的利用率,提高了系統性能。
雖然大部分情況下stream是容器調用Collection.stream()方法得到的,但stream和collections有以下不同:
無存儲。stream不是一種數據結構,它只是某種數據源的一個視圖,數據源可以是一個數組,Java容器或I/O channel等。
為函數式編程而生。對stream的任何修改都不會修改背后的數據源,比如對stream執行過濾操作并不會刪除被過濾的元素,而是會產生一個不包含被過濾元素的新stream。
惰式執行。stream上的操作并不會立即執行,只有等到用戶真正需要結果的時候才會執行。
可消費性。stream只能被“消費”一次,一旦遍歷過就會失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成。
對stream的操作分為為兩類,中間操作(intermediate operations)和結束操作(terminal operations),二者特點是:
中間操作總是會惰式執行,調用中間操作只會生成一個標記了該操作的新stream,僅此而已。
結束操作會觸發實際計算,計算發生時會把所有中間操作積攢的操作以pipeline的方式執行,這樣可以減少迭代次數。計算完成之后stream就會失效。
響應式編程的概念
響應式編程是一種關注于數據流data streams)和事件傳遞的異步編程方式。它是面向對象編程中的“觀察者模式”的在多線程異步編程一種實現。
在響應式流中,當有新的數據到來的時候,由發布者(Publisher) 通知訂閱者(Subscriber)。此外,對推送來的數據的操作 是通過一種聲明式(declaratively)而不是命令式(imperatively)的方式表達的:開發者通過 描述“控制流程”來定義對數據流的處理邏輯。
除了數據推送以外,響應式編程對錯誤處理(error handling)和完成(completion)的定義實現也很完善。 一個 可以推送新的數據到它的 (調用 方法), 同樣也可以推送錯誤(調用 方法)和完成(調用 方法)信號。 錯誤和完成信號都可以終止響應式流。
響應式編程的背壓機制
背壓是指在異步場景中,被觀察者發送事件速度遠快于觀察者的處理速度的情況下,一種告訴上游的被觀察者降低發送速度的策略。簡而言之,背壓是流速控制的一種策略。
理解了背壓的概念,在整體設計的時候就要考慮流水線上的每一個操作的時候就知道該怎么設計,盡量每個環節的執行時間大約一致,保證整體的處理速度比較均衡。
reactor的概念
Project Reactor(以下簡稱“Reactor”)與Spring是兄弟項目,側重于Server端的響應式編程,主要 artifact 是 reactor-core,這是一個基于 Java 8 的實現了響應式流規范 (Reactive Streams specification)的響應式庫。
Reactor中的發布者(Publisher)由和兩個類定義,它們都提供了豐富的操作符(operator)。一個Flux對象代表一個包含0..N個元素的響應式序列,而一個Mono對象代表一個包含零/一個(0..1)元素的結果。
既然是“數據流”的發布者,Flux和Mono都可以發出三種“數據信號”:元素值、錯誤信號、完成信號,錯誤信號和完成信號都是終止信號,完成信號用于告知下游訂閱者該數據流正常結束,錯誤信號終止數據流的同時將錯誤傳遞給下游訂閱者。