Spring Boot (十四): 響應式編程以及 Spring Boot Webflux 快速入門


1. 什么是響應式編程

在計算機中,響應式編程或反應式編程(英語:Reactive programming)是一種面向數據流和變化傳播的編程范式。這意味着可以在編程語言中很方便地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值通過數據流進行傳播。

例如,在命令式編程環境中,a=b+c 表示將表達式的結果賦給 a,而之后改變 b 或 c 的值不會影響 a 。但在響應式編程中,a 的值會隨着 b 或 c 的更新而更新。

響應式編程是基於異步和事件驅動的非阻塞程序,只需要在程序內啟動少量線程擴展,而不是水平通過集群擴展。

設想一個場景,從底層數據庫驅動,經過持久層、服務層、MVC層中的model,到用戶的前端界面的元素,全部都采用聲明式的編程范式,從而搭建一條能夠傳遞變化的管道,這樣我們只要更新一下數據庫中的數據,用戶的界面上就相應的發生變化,從而無需前端輪詢才能獲取到最新的數據。

簡單來講,我們以前寫的程序是阻塞式的,當一個請求任務過來時,線程會阻塞,等到這個任務完成后再返回出去。而響應式編程則是一個請求任務過來時,會有其他的線程去做處理,當任務執行結束后再異步的通知回去。

2. 為什么要使用響應式編程

在如今互聯網時代的大背景下,Web應用通常要面對高並發、海量數據的挑戰,性能從來都是必須要考量的核心因素。

阻塞便是性能殺手之一。

多數人不認為阻塞是一個比較大的問題,至少覺得除了網絡I/O之外,讀寫文件和數據庫還是很快的,許多人也一直在寫阻塞的代碼。

那么 I/O 操作具體有多慢?

2.1 CPU眼中的時間

以下內容來源 https://blog.csdn.net/get_set/article/details/79466402

CPU絕對稱得上是“閃電俠”,因為它們做事都有自己的一套時鍾。我們故事的主人公是一個主頻為2.5GHz的CPU,如果它的世界也有“秒”的概念,並且它的時鍾跳一下為一秒,那么在CPU(CPU的一個核心)眼中的時間概念是啥樣的呢?

CPU先生所在的組是硬件部計算組。對它來說,與其一起緊密合作的幾個小伙伴還能跟的上它的節奏:

  • CPU先生很利索,只需要一秒就可以完成一個指令,復雜的動作可能需要多個指令。
  • 好在“貼身秘書”一級緩存反應比較快,能夠秒懂CPU先生的意思。
  • 來自“秘書組”的二級緩存雖然要十幾秒才能“get”到CPU先生的點,但也不算太遲鈍。
  • 和內存組的合作已經習以為常了,跟內存請求的數據通常要4-5分鍾才能找到(內存尋址),不過也還好啦,畢竟一級緩存那里能拿到80%想要的數據,其余的二級緩存也能搞定一大部分,不怎么耽誤事兒。

CPU先生是典型的工作狂,任務多的時候,通宵達旦也毫無怨言,但是有什么事情讓它等,那簡直要他命了。恰恰一起共事的其他組(尤其是I/O組的磁盤和網卡)相對來說那效率是低的離譜啊:

  • 關於I/O組的同事,CPU先生已經抱怨很久了,每次找SSD要東西,都要花費4-5天才能找到(尋址),等到數據傳送過來,幾周都過去了。機械磁盤更是過分地離譜,跟他要個數據,竟然要平均花費10個月才能找到,如果要讀取1M的數據,竟然要20個月!這種員工怎么還不下崗?!
  • 關於網卡,CPU先生知道它們也盡力了,畢竟萬兆網絡成本頗高。與機房內的其他小伙伴們用千兆網絡互相溝通也算順暢,給另一台機器的CPU朋友發送1K的書信,最快七八個小時就可以送過去了。但是1K的書信經過層層包裹,實際也寫不了多少話。更要命的是,網卡們的溝通手續繁雜,每次網絡溝通前的 “你好能聽到我嗎?——我能聽到,你那邊能聽到我嗎?——我也能聽到你,那我們開始吧!” 這樣的握手確認都要花掉很長的時間,不過不能當面溝通,也只能這樣了。這還好,最恐怖的是與其他城市的小伙伴溝通,有時候傳遞消息要花費好幾年呢!

由此可見,對於CPU先生來說,想要讓工作充實起來實在不容易,不過多虧了內存組的小伙伴幫忙分批緩存往返於I/O組的數據,矛盾才有所緩解。

這個圖只能明顯看出涉及I/O的時間條,我們轉換成對數刻度的圖看一下:

這個圖並不是直觀的比例,橫軸上每個刻度是一個數量級,可見I/O的速度與CPU和內存相比是要差幾個數量級的。由此可見,對於大型高並發場景下的Web應用,緩存有多重要,更高的緩存命中率就意味着性能。

  1. 並行化:使用更多的線程和硬件資源;
  2. 異步化:基於現有的資源來提高執行效率。

3. 基礎概念

在介紹主題之前先普及幾個概念:

3.1 Backpressure(背壓)

背壓是一種常用策略,使得發布者擁有無限制的緩沖區存儲元素,用於確保發布者發布元素太快時,不會去壓制訂閱者。

3.2 Reactive Streams(響應式流)

一般由以下組成:

  • 發布者:發布元素到訂閱者
  • 訂閱者:消費元素
  • 訂閱:在發布者中,訂閱被創建時,將與訂閱者共享
  • 處理器:發布者與訂閱者之間處理數據

3.3 Mono 和 Flux

  • Mono:實現發布者,並返回 0 或 1 個元素
  • Flux:實現發布者,並返回 N 個元素

4. Spring Webflux

Spring Boot Webflux 是基於 Reactor 實現的。Spring Boot 2.0 包括一個新的 spring-webflux 模塊。該模塊包含對響應式 HTTP 和 WebSocket 客戶端的支持,以及對 REST,HTML 和 WebSocket 交互等程序的支持。一般來說,Spring MVC 用於同步處理,Spring Webflux 用於異步處理。

Spring Boot Webflux 有兩種編程模型實現,一種類似 Spring MVC 注解方式,另一種是使用其功能性端點方式。

4.1 適用性

一圖就很明確了,WebFlux 和 MVC 有交集。但是注意:

  • MVC 能滿足場景的,就不需要更改為 WebFlux。
  • 要注意容器的支持,可以看看下面內嵌容器的支持。
  • 微服務體系結構,WebFlux 和 MVC 可以混合使用。尤其開發 IO 密集型服務的時候,選擇 WebFlux 去實現。

4.2 內嵌容器

跟 Spring Boot 大框架一樣啟動應用,但 WebFlux 默認是通過 Netty 啟動,並且自動設置了默認端口為 8080。另外還提供了對 Jetty、Undertow 等容器的支持。開發者自行在添加對應的容器 Starter 組件依賴,即可配置並使用對應內嵌容器實例。

但是要注意,必須是 Servlet 3.1+ 容器,如 Tomcat、Jetty;或者非 Servlet 容器,如 Netty 和 Undertow。

4.3 數據庫

支持 reactive 編程的數據庫只有 MongoDB , redis , Cassandra , Couchbase 。

4.4 快速上手

工程依賴

代碼清單:spring-boot-webflux/pom.xml


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Service 類

代碼清單:spring-boot-webflux/src/main/java/com/springboot/springbootwebflux/service/impl/UserServiceImpl.java


@Service
public class UserServiceImpl implements UserSerivice {

    private static Map<Long, User> map = new HashMap<>();

    static {
        map.put(1L, new User(1L, "www.geekdigging.com", 18));
        map.put(2L, new User(2L, "極客挖掘機", 28));
    }

    @Override
    public Mono<User> getUserById(Long id) {

        return Mono.just(map.get(id));
    }
}

Controller 類

代碼清單:spring-boot-webflux/src/main/java/com/springboot/springbootwebflux/controller/UserController.java


@RestController
public class UserController {

    @Autowired
    UserSerivice userSerivice;

    @GetMapping("/getUserById/{id}")
    public Mono<User> getUserById(@PathVariable Long id) {
        return userSerivice.getUserById(id);
    }
}

通過上面的示例可以發現,開發模式和之前 Spring MVC 的模式差別不是很大,只是在方法的返回值上有所區別。

5. 示例代碼

示例代碼-Github

示例代碼-Gitee

6. 參考

https://blog.csdn.net/get_set/article/details/79466402

http://www.ityouknow.com/springboot/2019/02/12/spring-boot-webflux.html

https://www.cnblogs.com/limuma/p/9315442.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM