文章首發自個人公眾號: 小哈學Java
目錄
一、什麼是 Spring WebFlux
二、WebFlux 的優勢&提升效能?
三、WebFlux 應用場景
四、選 WebFlux 還是 Spring MVC?
五、異同點
六、簡單看看 WebFlux 是如何分發請求的
七、快速入門
-
7.1 新增 webflux 依賴
-
7.2 定義介面
-
7.3 測試介面
八、總結
九、GitHub 示例程式碼
一、什麼是 Spring WebFlux
下圖截自 Spring Boot 官方網站:
結合上圖,在瞭解 Spring WebFlux 之前,我們先來對比說說什麼是 Spring MVC,這更有益我們去理解 WebFlux,圖右邊對 Spring MVC 的定義,原文如下:
Spring MVC is built on the Servlet API and uses a synchronous blocking I/O architecture whth a one-request-per-thread model.
翻譯一下,意思如下:
Spring MVC 構建於 Servlet API 之上,使用的是同步阻塞式 I/O 模型,什麼是同步阻塞式 I/O 模型呢?就是說,每一個請求對應一個執行緒去處理。
瞭解了 Spring MVC 之後,再來說說 Spring WebFlux:
上圖左邊,官方給出的定義如下:
Spring WebFlux is a non-blocking web framework built from the ground up to take advantage of multi-core, next-generation processors and handle massive numbers of concurrent connections.
翻譯一下,內容如下:
Spring WebFlux 是一個非同步非阻塞式的 Web 框架,它能夠充分利用多核 CPU 的硬體資源去處理大量的併發請求。
二、WebFlux 的優勢&提升效能?
WebFlux 內部使用的是響應式程式設計(Reactive Programming),以 Reactor 庫為基礎, 基於非同步和事件驅動,可以讓我們在不擴充硬體資源的前提下,提升系統的吞吐量和伸縮性。
看到這裡,你是不是以為 WebFlux 能夠使程式執行的更快呢?量化一點,比如說我使用 WebFlux 以後,一個介面的請求響應時間是不是就縮短了呢?
**抱歉了,答案是否定的!**以下是官方原話:
Reactive and non-blocking generally do not make applications run faster.
WebFlux 並不能使介面的響應時間縮短,它僅僅能夠提升吞吐量和伸縮性。
三、WebFlux 應用場景
上面說到了, Spring WebFlux 是一個非同步非阻塞式的 Web 框架,所以,它特別適合應用在 IO 密集型的服務中,比如微服務閘道器這樣的應用中。
PS: IO 密集型包括:磁碟IO密集型, 網路IO密集型,微服務閘道器就屬於網路 IO 密集型,使用非同步非阻塞式程式設計模型,能夠顯著地提升閘道器對下游服務轉發的吞吐量。
四、選 WebFlux 還是 Spring MVC?
首先你需要明確一點就是:WebFlux 不是 Spring MVC 的替代方案!,雖然 WebFlux 也可以被執行在 Servlet 容器上(需是 Servlet 3.1+ 以上的容器),但是 WebFlux 主要還是應用在非同步非阻塞程式設計模型,而 Spring MVC 是同步阻塞的,如果你目前在 Spring MVC 框架中大量使用非同步方案,那麼,WebFlux 才是你想要的,否則,使用 Spring MVC 才是你的首選。
在微服務架構中,Spring MVC 和 WebFlux 可以混合使用,比如已經提到的,對於那些 IO 密集型服務(如閘道器),我們就可以使用 WebFlux 來實現。
選 WebFlux 還是 Spring MVC? This is not a problem!
我們不能為了裝逼而裝逼,為了技術而技術,還要考量到轉向非阻塞響應式程式設計學習曲線是陡峭的,小組成員的學習成本等諸多因素。
總之一句話,在合適的場景中,選型最合適的技術。
五、異同點
從上圖中,可以一眼看出 Spring MVC 和 Spring WebFlux 的相同點和不同點:
相同點:
- 都可以使用 Spring MVC 註解,如
@Controller
, 方便我們在兩個 Web 框架中自由轉換; - 均可以使用 Tomcat, Jetty, Undertow Servlet 容器(Servlet 3.1+);
- ...
注意點:
- Spring MVC 因為是使用的同步阻塞式,更方便開發人員編寫功能程式碼,Debug 測試等,一般來說,如果 Spring MVC 能夠滿足的場景,就儘量不要用 WebFlux;
- WebFlux 預設情況下使用 Netty 作為伺服器;
- WebFlux 不支援 MySql;
六、簡單看看 WebFlux 是如何分發請求的
使用過 Spring MVC 的小夥伴們,應該到知道 Spring MVC 的前端控制器是 DispatcherServlet
, 而 WebFlux 是 DispatcherHandler
,它實現了 WebHandler
介面:
來看看DispatcherHandler
類中處理請求的 handle
方法:
- ①:
ServerWebExchange
物件中放置每一次 HTTP 請求響應資訊,包括引數等; - ②: 判斷整個介面對映
mappings
集合是否為空,空則建立一個 Not Found 的錯誤; - ③: 根據具體的請求地址獲取對應的
handlerMapping
; - ④: 呼叫具體業務方法,也就是我們定義的介面方法;
- ⑤: 處理返回的結果;
七、快速入門
7.1 新增 webflux 依賴
新建一個 Spring Boot 專案,新建步驟可參考筆者另一篇博文《Spring Boot 入門教程 | 圖文講解》,在 pom.xml
檔案中新增 webflux
依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
複製程式碼
7.2 定義介面
新建一個 controller
包,用來放置對外的介面類,再建立一個 HelloWebFluxController.class
類,定義兩個介面:
package site.exception.springbootwebfluxhello.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import site.exception.springbootwebfluxhello.entity.User;
/**
* @author 犬小哈 (微訊號: 小哈學Java)
* @site 個人網站: www.exception.site
* @date 2019/4/15
* @time 下午9:12
* @discription
**/
@RestController
public class HelloWebFluxController {
@GetMapping("/hello")
public String hello() {
return "Hello, WebFlux !";
}
@GetMapping("/user")
public Mono<User> getUser() {
User user = new User();
user.setName("犬小哈");
user.setDesc("歡迎關注我的公眾號: 小哈學Java");
return Mono.just(user);
}
}
複製程式碼
User.java
:
package site.exception.springbootwebfluxhello.entity;
/**
* @author 犬小哈 (微訊號: 小哈學Java)
* @site 個人網站: www.exception.site
* @date 2019/4/15
* @time 下午9:12
* @discription
**/
public class User {
/**
* 姓名
*/
private String name;
/**
* 描述
*/
private String desc;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
複製程式碼
以上控制器類中,我們使用的全都是 Spring MVC 的註解,分別定義了兩個介面:
- 一個 GET 請求的
/hello
介面,返回Hello, WebFlux !
字串。 - 又定義了一個 GET 請求的
/user
方法,返回的是 JSON 格式User
物件。
這裡注意,User
物件是通過 Mono
物件包裝的,你可能會問,為啥不直接返回呢?
在 WebFlux 中,Mono
是非阻塞的寫法,只有這樣,你才能發揮 WebFlux 非阻塞 + 非同步的特性。
補充:在 WebFlux 中,除了
Mono
外,還有一個Flux
,這哥倆均能充當響應式程式設計中釋出者的角色,不同的是:
Mono
:返回 0 或 1 個元素,即單個物件。Flux
:返回 N 個元素,即 List 列表物件。
7.3 測試介面
啟動專案,檢視控制檯輸出:
當控制檯中輸出中包含 Netty started on port(s): 8080
語句時,說明預設使用 Netty 服務已經啟動了。
開啟瀏覽器,先對 /user
介面發起呼叫:
返回成功。
再來對 /user
介面測試一下:
返回 JSON 格式的 User 實體也是 OK 的!
八、總結
本文中,我們學習了什麼是 Spring WebFlux, WebFlux 的優勢和應用場景,接下來我麼談了談在我們生產環境中技術選型該選 WebFlux 還是 Spring MVC, 兩者之間又有什麼異同點,以及從原始碼角度瞭解了 WebFlux 是如何分發請求的。
最後上手操作寫了兩個簡單的介面,並測試成功了。
下一章中,我們將進一步學習,如何在 WebFlux 中對資料庫做增刪改查操作,敬請期待吧!