Spring Boot Spring WebFlux

初探 Spring WebFlux

陳冠丞 Lio Chen 2019/12/26 09:46:17
616

前言

Spring WebFlux是Spring Framework 5.0中引入的Reactive Web框架,名稱中的 Flux 來源於 Reactor中的Flux。該模塊中包含了Reactive HTTP、Server推送事件和 WebSocket 的Client端和Server端的支持。對於開發人員來說,比較重要的是Server端的開發,這也是本文的重點。 與Spring MVC不同,它不需要Servlet API,完全非同步和非阻塞, 並通過Reactor實現Reactive Streams規範,所以性能更高。 並且可以在諸如Netty,Undertow和Servlet 3.1+容器的服務器上運行。

一、介紹

Spring WebFlux特性:

1.響應式函數編程:

Spring WebFlux支援函數式編程,透過Reactor框架實現。

2.不必再使用Servlet容器:

在以前Spring MVC都運行於Servlet容器之中,例如Tomcat, Jetty…等等。而Spring WebFlux不僅能運行於傳統的Servlet3.1以上容器中,還能運行在支持NIO的Netty和Undertow中。

3.非同步Asynchronous/非阻塞Non-blocking:

學過Spring MVC的人都知道,Spring MVC是同步/阻塞的IO模式,相對來說是比較資源浪費的,如果在處理一個比較耗時的任務時,例如:上傳一個比較大的檔案,一開始Server一直在等待接收檔案,等到檔案接收完成,接下來又要將檔案寫入磁碟,在寫入的過程中,Server需要等到檔案寫完才能去做其它的事情。在這每個處理的過程中等待,浪費不少資源。而Spring WebFlux可以做到非同步非阻塞來解這樣的問題。當發現檔案還沒準備好,就先去做其它事情,當檔案準備好之後,通知Server來處理,當接收完畢寫入磁碟的時候,寫入完畢後通知Server再來處理。相對Spring MVC而言,可以節省系統資源以及支援更高的並發量。

Spring WebFlux Mono和Flux介紹:

Flux 和 Mono 是 Reactor 中的兩個基本概念。Flux 表示的是包含 0 到 N 個元素的非同步序列。 在該序列中可以包含三種不同類型的消息通知:正常的包含元素的消息、序列結束的消息和序列出錯的消息。 當消息通知產生時,訂閱者中對應的方法 onNext(), onComplete()和 onError()會被調用。Mono 表示的是包含 0 或者 1 個元素的非同步序列。 該序列中同樣可以包含與 Flux 相同的三種類型的消息通知。Flux 和 Mono 之間可以進行轉換。 對一個 Flux 序列進行計數操作,得到的結果是一個 Mono對象。把兩個 Mono 序列合並在一起,得到的是一個 Flux 對象。

在 Spring 官方網站首頁中,有個技術堆疊示意圖,很清楚的解釋來了 Spring WebFlux 跟 Spring MVC的差異!

二、環境設定

跟之前 Spring MVC 相同進入Spring Initializr的但是將Web部分改選用Spring Reactive Web就可以打包檔案匯入到你常用的IDE上使用即可

三、實際操作

那麼,來 Say 個 Hello 吧!

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import reactor.core.publisher.Mono;

@SpringBootApplication
@Controller
public class SpringWebFluxApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringWebFluxApplication.class, args);
    }

    @GetMapping("/iam/{name}") 
    public Mono<String> hello(@PathVariable("name") String name) {
        return Mono.just("Hello " + name);
    }
}

可以到看跟 Spring MVC 沒什麼兩樣,如前所述。

這是因為 WebFlux 可以採用 Spring MVC 註解模式,在官方的〈Web on Reactive Stack〉中有張圖,示意了兩者重疊之處:

當然,還是有不同的地方,也就是 iam 方法的傳回值是 Reactor 的 Mono 型態,WebFlux 本身會訂閱 Flux 或 Mono,在有資料的時候,對客戶端進行回應,在資料流結束後,關閉客戶端的連線,Spring Boot 預設會使用 Netty,例如,若請求 http://localhost:8080/iam/Lio,回應會如下圖

再來寫一個Flux的範例

  /**
   * 使用flux,用Stream返回0-N個元素
   */
  @GetMapping(value = "/flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
  public Flux<String> flux() {
      long timeMillis = System.currentTimeMillis();
      log.info("webflux() start");
      Flux<String> result = Flux.fromStream(IntStream.range(1, 5).mapToObj(I -> {
          try {
              TimeUnit.SECONDS.sleep(1);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          return "flux data—" + I;
      }));
      log.info("flux() end use time {}/ms", System.currentTimeMillis() - timeMillis);
      return result;
  }

這個在瀏覽器上會每隔一秒接收一行數據如下圖

 

四、結論

大多數人以為,Spring WebFlux 是 Spring MVC 的替代方案,實際上不是這樣的,雖然 Spring WebFlux 也可以執行在 Servlet 容器上(須在Servlet 3.1+ 以上的容器實作),然而Spring WebFlux 重點是著重在非同步非阻斷,而 Spring MVC 是針對同步,如果你曾經因為得在同步的 Web 容器之中,全面或大量地設計非同步方案而感到諸多不便,那 Spring WebFlux 會是你想要的。 採用 Spring WebFlux,難處並不在於 API,實際上,你已經學會一半的 Spring WebFlux 了,因為 Spring WebFlux 可以採用與 Spring MVC 相同的註解模式來實作(另一個模式是函數式風格),也就是說,大部份在 Spring MVC 中使用的註解,都可以在 Web Flux 中使用,另一方面,Spring WebFlux 基於 Spring 自家的 Reactive 實作,也就是 Reactor 專案,因此接觸 Spring WebFlux 時,學過Reactor也是能很快就能上手了。

參考資源

陳冠丞 Lio Chen