白菜Java自習室 涵蓋核心知識前端
下圖截自 Spring Boot 官方網站:web
結合上圖,在瞭解 Spring WebFlux 以前,咱們先來對比說說什麼是 Spring MVC,這更有益咱們去理解 WebFlux,圖右邊對 Spring MVC 的定義,原文以下:spring
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 模型呢?就是說,每個請求對應一個線程去處理。json
瞭解了 Spring MVC 以後,再來講說 Spring WebFlux:服務器
上圖左邊,官方給出的定義以下:markdown
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 內部使用的是響應式編程(Reactive Programming),以 Reactor 庫爲基礎, 基於異步和事件驅動,可讓咱們在不擴充硬件資源的前提下,提高系統的吞吐量和伸縮性。併發
看到這裏,你是否是覺得 WebFlux 可以使程序運行的更快呢?量化一點,好比說我使用 WebFlux 之後,一個接口的請求響應時間是否是就縮短了呢?
抱歉了,答案是否認的!如下是官方原話:
Reactive and non-blocking generally do not make applications run faster.
WebFlux 並不能使接口的響應時間縮短,它僅僅可以提高吞吐量和伸縮性。
上面說到了, Spring WebFlux 是一個異步非阻塞式的 Web 框架,因此,它特別適合應用在 IO 密集型的服務中,好比微服務網關這樣的應用中。
PS: IO 密集型包括:磁盤IO密集型, 網絡IO密集型,微服務網關就屬於網絡 IO 密集型,使用異步非阻塞式編程模型,可以顯著地提高網關對下游服務轉發的吞吐量。
首先你須要明確一點就是: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 的相同點和不一樣點:
相同點:
@Controller
, 方便咱們在兩個 Web 框架中自由轉換;注意點:
使用過 Spring MVC 的小夥伴們,應該到知道 Spring MVC 的前端控制器是 DispatcherServlet, 而 WebFlux 是 DispatcherHandler,它實現了 WebHandler 接口:
public interface WebHandler {
Mono<Void> handle(ServerWebExchange var1);
}
複製代碼
來看看DispatcherHandler類中處理請求的 handle 方法:
public Mono<Void> handle(ServerWebExchange exchange) {
return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
return mapping.getHandler(exchange);
}).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
return this.invokeHandler(exchange, handler);
}).flatMap((result) -> {
return this.handleResult(exchange, result);
});
}
複製代碼
新建一個 Spring Boot 項目,在 pom.xml 文件中添加 webflux 依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
複製代碼
新建一個 controller 包,用來放置對外的接口類,再建立一個 WebFluxController 類,定義兩個接口:
@RestController
public class WebFluxController {
@GetMapping("/hello")
public String hello() {
return "Hello, WebFlux !";
}
@GetMapping("/user")
public Mono<UserDO> getUser() {
UserDO user = new UserDO();
user.setName("白菜");
user.setDesc("白菜Java自習室");
return Mono.just(user);
}
}
複製代碼
@Data
public class UserDO {
/**
* 姓名
*/
private String name;
/**
* 描述
*/
private String desc;
}
複製代碼
以上控制器類中,咱們使用的全都是 Spring MVC 的註解,分別定義了兩個接口:
/hello
接口,返回 Hello, WebFlux !字符串。/user
方法,返回的是 JSON 格式 User 對象。這裏注意,User 對象是經過 Mono 對象包裝的,你可能會問,爲啥不直接返回呢?
在 WebFlux 中,Mono 是非阻塞的寫法,只有這樣,你才能發揮 WebFlux 非阻塞 + 異步的特性。
在 WebFlux 中,除了 Mono 外,還有一個 Flux,不一樣的是:
- Mono:返回 0 或 1 個元素,即單個對象。
- Flux:返回 N 個元素,即 List 列表對象。
啓動項目,查看控制檯輸出:
當控制檯中輸出中包含 Netty started on port(s): 8080 語句時,說明默認使用 Netty 服務已經啓動了。
先對 /hello
接口發起調用:
GET http://localhost:8080/hello
Accept: application/json
複製代碼
GET http://localhost:8080/hello
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 16
Hello, WebFlux !
Response code: 200 (OK); Time: 681ms; Content length: 16 bytes
複製代碼
再來對 /user
接口測試一下:
GET http://localhost:8080/user
Accept: application/json
複製代碼
GET http://localhost:8080/user
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 46
{
"name": "白菜",
"desc": "白菜Java自習室"
}
Response code: 200 (OK); Time: 148ms; Content length: 32 bytes
複製代碼