學習WebFlux時常見的問題

前言

只有光頭才能變強。html

文本已收錄至個人GitHub精選文章,歡迎Starhttps://github.com/ZhongFuCheng3y/3yreact

回顧一下上篇我對WebFlux的入門,若是沒讀過的同窗建議讀一下再來看本篇文章,上一篇文章花了我不少的心血~~git

開局再來一張圖,內容全靠編:github

這篇主要寫寫我初學時對WebFlux的一些疑問,不知道你們在看上一篇文章的時候有沒有相應的問題呢?web

此次學WebFlux主要的動力是公司組內分享,寫了一個PPT,有須要的同窗在個人公衆號(Java3y)下回復「PPT」便可獲取。spring

1、原本就能實現異步非阻塞,爲啥要用WebFlux?

相信有過相關了解的同窗都知道,Servlet 3.1就已經支持異步非阻塞了。sql

咱們能夠以自維護線程池的方式實現異步數據庫

  • 說白了就是Tomcat的線程處理請求,而後把這個請求分發到自維護的線程處理,Tomcat的請求線程返回
1@WebServlet(value = "/nonBlockingThreadPoolAsync", asyncSupported = true)
 2public class NonBlockingAsyncHelloServlet extends HttpServlet {
 3
 4    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));
 5
 6    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 7
 8        AsyncContext asyncContext = request.startAsync();
 9
10        ServletInputStream inputStream = request.getInputStream();
11
12        inputStream.setReadListener(new ReadListener() {
13            @Override
14            public void onDataAvailable() throws IOException {
15
16            }
17            @Override
18            public void onAllDataRead() throws IOException {
19                executor.execute(() -> {
20                    new LongRunningProcess().run();
21
22                    try {
23                        asyncContext.getResponse().getWriter().write("Hello World!");
24                    } catch (IOException e) {
25                        e.printStackTrace();
26                    }
27                    asyncContext.complete();
28
29                });
30            }
31
32            @Override
33            public void onError(Throwable t) {
34                asyncContext.complete();
35            }
36        });
37
38
39    }
40
41}

流程圖以下:編程

異步非阻塞的圖

異步非阻塞的圖api

上面的例子來源:

簡單的方式,咱們還可使用JDK 8 提供的CompletableFuture類,這個類能夠方便的處理異步調用。

1protected void doGet(HttpServletRequest request,
 2                     HttpServletResponse response) throws ServletException, IOException {
 3    long t1 = System.currentTimeMillis();
 4
 5    // 開啓異步
 6    AsyncContext asyncContext = request.startAsync();
 7
 8    // 執行業務代碼(doSomething 指的是處理耗費時間長的方法)
 9    CompletableFuture.runAsync(() -> doSomeThing(asyncContext,
10                                                 asyncContext.getRequest(), asyncContext.getResponse()));
11
12    System.out.println("async use:" + (System.currentTimeMillis() - t1));
13}

要處理複雜的邏輯時,不管是回調或 CompletableFuture在代碼編寫上都會比較複雜(代碼量大,不易於看懂),而WebFlux使用的是Reactor響應式流,裏邊提供了一系列的API供咱們去處理邏輯,就很方便了。

回調地獄

回調地獄

更重要的是:

  • WebFlux使用起來能夠像使用SpringMVC同樣,可以大大減少學習成本
  • WebFlux也可使用Functional Endpoints方式編程,總的來講仍是要比回調/CompletableFuture要簡潔和易編寫。

無縫與SpringMVC的技術使用

無縫與SpringMVC的技術使用

值得一提的是:

若是Web容器使用的是Tomcat,那麼就是使用Reactor橋接的servlet async api
若是Web容器是Netty,那麼就是使用的Netty,天生支持Reactive

官方的推薦是使用Netty跑WebFlux

2、WebFlux性能的問題

咱們從上篇文章中就發現,瀏覽器去調用處理慢的接口,不管是該接口是同步的,仍是說是異步的,返回到瀏覽器的時間都是一致的

  • 同步:服務器接收到請求,一個線程會處理請求,直到該請求處理完成,返回給瀏覽器
  • 異步:服務器接收到請求,一個線程會處理請求,而後指派別的線程處理請求,請求的線程直接空閒出來。

官網也說了:

Reactive and non-blocking generally do not make applications run faster

使用異步非阻塞的好處就是:

The key expected benefit of reactive and non-blocking is the ability to scale with a small, fixed number of threads and less memory.That makes applications more resilient under load, because they scale in a more predictable way

好處:只須要在程序內啓動少許線程擴展,而不是水平經過集羣擴展。異步可以規避文件IO/網絡IO阻塞所帶來的線程堆積

下面來看一下針對相同的請求量,同步阻塞和異步非阻塞的吞吐量和響應時長對比:

吞吐量和RT對比

吞吐量和RT對比

注:

  • 請求量不大時(3000左右),同步阻塞多線程處理請求,吞吐量和響應時長都沒落後。(按道理WebFlux可能還要落後一些,畢竟多作了一步處理--&gt;將請求委派給另外一個線程去作處理
  • 請求量大時,線程數不夠用,同步阻塞(MVC)只能等待,因此吞吐量要降低,響應時長要提升(排隊)。

Spring WebFlux在應對高併發的請求時,藉助於異步IO,可以以少許而穩定的線程處理更高吞吐量的請求,尤爲是當請求處理過程若是由於業務複雜或IO阻塞等致使處理時長較長時,對比更加顯著。

3、WebFlux實際應用

WebFlux須要非阻塞的業務代碼,若是阻塞,須要本身開線程池去運行。WebFlux什麼場景下能夠替換SpringMVC呢?

  • 想要內存和線程數較少的場景
  • 網絡較慢或者IO會常常出現問題的場景

SpringMVC和WebFlux更多的是互補關係,而不是替換。阻塞的場景該SpringMVC仍是SpringMVC,並非WebFlux出來就把SpringMVC取代了。

SpringMVC和WebFlux

SpringMVC和WebFlux

若是想要發揮出WebFlux的性能,須要從Dao到Service,所有都要是Mono和Flux,目前官方的數據層Reactive框架只支持Redis,Mongo等幾個,沒有JDBC

目前對於關係型數據庫Pivotal團隊開源出R2DBC(Reactive Relational Database Connectivity),其GitHub地址爲:

目前R2DBC支持三種數據源:

總的來講,由於WebFlux是響應式的,要想發揮出WebFlux的性能就得將代碼全改爲響應式的,而JDBC目前是沒支持的(至少MySQL還沒支持),而響應式的程序很差調試和編寫(相對於同步的程序),因此如今WebFlux的應用場景仍是相對較少的。

因此,我認爲在網關層用WebFlux比較合適(原本就是網絡IO較多的場景)

如今再回來看Spring官網的圖,是否是就更親切了?

Spring官網介紹圖

Spring官網介紹圖

參考資料:

4、有必要學Functional Endpoints 編程模式嗎?

前面也提到了,WebFlux提供了兩種模式供咱們使用,一種是SpringMVC 註解的,一種是叫Functional Endpoints

Lambda-based, lightweight, and functional programming model

總的來看,就是配合Lambda和流式編程去使用WebFlux。若是你問我:有必要學嗎?其實我以爲能夠先放着。我認爲如今WebFlux的應用場景仍是比較少,等真正用到的時候再學也不是什麼難事,反正就是學些API嘛~

有Lambda表達式和Stream流的基礎,等真正用到的時候再學也不是啥問題~

如下是經過註解的方式來使用WebFlux的示例:

經過註解的方式來使用WebFlux

經過註解的方式來使用WebFlux

如下是經過Functional Endpoints的方式來使用WebFlux的示例:

路由分發器,至關於註解的GetMapping…

路由分發器

路由分發器

UserHandler,至關於UserController:

UserHanler

UserHanler

5、WebFlux的實際使用場景

總的來講,由於WebFlux是響應式的,要想發揮出WebFlux的性能就得將代碼全改爲響應式的,而JDBC目前是沒支持的(至少MySQL還沒支持),而響應式的程序很差調試和編寫(相對於同步的程序),老項目也不太可能把依賴直接升上Spring5.0,因此如今WebFlux的應用場景仍是相對較少的(我的以爲)。

網關層用WebFlux比較合適(原本就是網絡IO較多的場景)

  • SpringCloud Gateway是基於WebFlux實現的

最後

此次學WebFlux主要的動力是公司組內分享,寫了一個PPT,有須要的同窗在個人公衆號(Java3y)下回復「PPT」便可獲取。

本已收錄至個人GitHub精選文章,歡迎Starhttps://github.com/ZhongFuCheng3y/3y

樂於輸出乾貨的Java技術公衆號:Java3y。公衆號內有300多篇原創技術文章、海量視頻資源、精美腦圖,關注便可獲取!

轉發到朋友圈是對我最大的支持!

轉發到朋友圈是對我最大的支持!

很是感謝人才們能看到這裏,若是這個文章寫得還不錯,以爲「三歪」我有點東西的話 求點贊 求關注️ 求分享👥 求留言💬 對暖男我來講真的 很是有用!!!

創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見!

相關文章
相關標籤/搜索