學習WebFlux時常見的問題

Java3y發表於2019-12-08

前言

只有光頭才能變強。

文字已收錄至我的GitHub精選文章,歡迎Stargithub.com/ZhongFuChen…

回顧一下上篇我對WebFlux的入門,如果沒讀過的同學建議讀一下再來看本篇文章,上一篇文章花了我很多的心血~~

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

學習WebFlux時常見的問題

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

這次學WebFlux主要的動力是公司組內分享,寫了一個PPT,有需要的同學在我的公眾號(Java3y)下回復“PPT”即可獲取。

一、本來就能實現非同步非阻塞,為啥要用WebFlux?

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

我們可以以自維護執行緒池的方式實現非同步

  • 說白了就是Tomcat的執行緒處理請求,然後把這個請求分發到自維護的執行緒處理,Tomcat的請求執行緒返回
@WebServlet(value = "/nonBlockingThreadPoolAsync", asyncSupported = true)
public class NonBlockingAsyncHelloServlet extends HttpServlet {

    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        AsyncContext asyncContext = request.startAsync();

        ServletInputStream inputStream = request.getInputStream();

        inputStream.setReadListener(new ReadListener() {
            @Override
            public void onDataAvailable() throws IOException {

            }
            @Override
            public void onAllDataRead() throws IOException {
                executor.execute(() -> {
                    new LongRunningProcess().run();

                    try {
                        asyncContext.getResponse().getWriter().write("Hello World!");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    asyncContext.complete();

                });
            }

            @Override
            public void onError(Throwable t) {
                asyncContext.complete();
            }
        });


    }

}
複製程式碼

流程圖如下:

非同步非阻塞的圖

上面的例子來源:

簡單的方式,我們還可以使用JDK 8 提供的CompletableFuture類,這個類可以方便的處理非同步呼叫。

protected void doGet(HttpServletRequest request,
                     HttpServletResponse response) throws ServletException, IOException {
    long t1 = System.currentTimeMillis();

    // 開啟非同步
    AsyncContext asyncContext = request.startAsync();

    // 執行業務程式碼(doSomething 指的是處理耗費時間長的方法)
    CompletableFuture.runAsync(() -> doSomeThing(asyncContext,
                                                 asyncContext.getRequest(), asyncContext.getResponse()));

    System.out.println("async use:" + (System.currentTimeMillis() - t1));
}

複製程式碼

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

回撥地獄

更重要的是:

  • WebFlux使用起來可以像使用SpringMVC一樣,能夠大大減小學習成本
  • WebFlux也可以使用Functional Endpoints方式程式設計,總的來說還是要比回撥/CompletableFuture要簡潔和易編寫。

無縫與SpringMVC的技術使用

值得一提的是:

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

官方的推薦是使用Netty跑WebFlux

二、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對比

注:

  • 請求量不大時(3000左右),同步阻塞多執行緒處理請求,吞吐量和響應時長都沒落後。(按道理WebFlux可能還要落後一些,畢竟多做了一步處理-->將請求委派給另一個執行緒去做處理
  • 請求量大時,執行緒數不夠用,同步阻塞(MVC)只能等待,所以吞吐量要下降,響應時長要提高(排隊)。

Spring WebFlux在應對高併發的請求時,藉助於非同步IO,能夠以少量而穩定的執行緒處理更高吞吐量的請求,尤其是當請求處理過程如果因為業務複雜或IO阻塞等導致處理時長較長時,對比更加顯著。

三、WebFlux實際應用

WebFlux需要非阻塞的業務程式碼,如果阻塞,需要自己開執行緒池去執行。WebFlux什麼場景下可以替換SpringMVC呢?

  • 想要記憶體和執行緒數較少的場景
  • 網路較慢或者IO會經常出現問題的場景

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

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官網介紹圖

參考資料:

四、有必要學Functional Endpoints 程式設計模式嗎?

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

Lambda-based, lightweight, and functional programming model

總的來看,就是配合Lambda和流式程式設計去使用WebFlux。如果你問我:有必要學嗎?其實我覺得可以先放著。我認為現在WebFlux的應用場景還是比較少,等真正用到的時候再學也不是什麼難事,反正就是學些API嘛~

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

以下是通過註解的方式來使用WebFlux的示例:

通過註解的方式來使用WebFlux

以下是通過Functional Endpoints的方式來使用WebFlux的示例:

路由分發器,相當於註解的GetMapping...

路由分發器

UserHandler,相當於UserController:

UserHanler

五、WebFlux的實際使用場景

總的來說,因為WebFlux是響應式的,要想發揮出WebFlux的效能就得將程式碼全改成響應式的,而JDBC目前是沒支援的(至少MySQL還沒支援),而響應式的程式不好除錯和編寫(相對於同步的程式),老專案也不太可能把依賴直接升上Spring5.0,所以現在WebFlux的應用場景還是相對較少的(個人覺得)。

閘道器層用WebFlux比較合適(本來就是網路IO較多的場景)

  • SpringCloud Gateway是基於WebFlux實現的

最後

伺服器89/年,229/3年,買來送自己,送女朋友馬上過年再合適不過了,買了搭建個專案給面試官看也香,還可以熟悉技術棧,(老使用者用家人賬號買就好了,我用我女朋友的?)。掃碼或者點選購買

學習WebFlux時常見的問題

搭建教程,從0開始一步一步帶你搭建?

這次學WebFlux主要的動力是公司組內分享,寫了一個PPT,有需要的同學在我的公眾號(Java3y)下回復“PPT”即可獲取。

本已收錄至我的GitHub精選文章,歡迎Stargithub.com/ZhongFuChen…

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

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

非常感謝人才們能看到這裡,如果這個文章寫得還不錯,覺得「三歪」我有點東西的話 求點贊 求關注️ 求分享? 求留言? 對暖男我來說真的 非常有用!!!

創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見!

相關文章