為何現在響應式編程在業務開發微服務開發不普及


為何現在響應式編程在業務開發微服務開發不普及

主要因為數據庫 IO,不是 NIO。

不論是Java自帶的Future框架,還是 Spring WebFlux,還是 Vert.x,他們都是一種非阻塞的基於Ractor模型的框架(后兩個框架都是利用netty實現)。

在阻塞編程模式里,任何一個請求,都需要一個線程去處理,如果io阻塞了,那么這個線程也會阻塞在那。但是在非阻塞編程里面,基於響應式的編程,線程不會被阻塞,還可以處理其他請求。舉一個簡單例子:假設只有一個線程池,請求來的時候,線程池處理,需要讀取數據庫 IO,這個 IO 是 NIO 非阻塞 IO,那么就將請求數據寫入數據庫連接,直接返回。之后數據庫返回數據,這個鏈接的 Selector 會有 Read 事件准備就緒,這時候,再通過這個線程池去讀取數據處理(相當於回調),這時候用的線程和之前不一定是同一個線程。這樣的話,線程就不用等待數據庫返回,而是直接處理其他請求。這樣情況下,即使某個業務 SQL 的執行時間長,也不會影響其他業務的執行。

但是,這一切的基礎,是 IO 必須是非阻塞 IO,也就是 NIO(或者 AIO)。官方JDBC沒有 NIO,只有 BIO 實現。這樣無法讓線程將請求寫入鏈接之后直接返回,必須等待響應。但是也就解決方案,就是通過其他線程池,專門處理數據庫請求並等待返回進行回調,也就是業務線程池 A 將數據庫 BIO 請求交給線程池B處理,讀取完數據之后,再交給 A 執行剩下的業務邏輯。這樣A也不用阻塞,可以處理其他請求。但是,這樣還是有因為某個業務 SQL 的執行時間長,導致B所有線程被阻塞住隊列也滿了從而A的請求也被阻塞的情況,這是不完美的實現。真正完美的,需要 JDBC 實現 NIO。

Java 自帶的 Future 框架可以這么用JDBC:

@GetMapping
public DeferredResult<Result> get() {
DeferredResult<Result> deferredResult = new DeferredResult<>();
CompletableFuture.supplyAsync(() -> {
            return 阻塞數據庫IO;
        //dbThreadPool用來處理阻塞的數據庫IO
        }, dbThreadPool).thenComposeAsync(result -> {
    //spring 的 DeferredResult 來實現異步回調寫入結果返回
    deferredResult.setResult(result);
});
return deferredResult;
}

WebFlux 也可以使用阻塞JDBC,但是同理:

@GetMapping
public Mono<Result> get() {
return Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
            return 阻塞數據庫IO;
        //dbThreadPool用來處理阻塞的數據庫IO
        }, dbThreadPool));
}

Vert.x 也可以使用阻塞的JDBC,也是同理:

@GetMapping
public  DeferredResult<Result> get() {
DeferredResult<Result> deferredResult = new DeferredResult<>();
getResultFromDB().setHandler(asyncResult -> {
            if (asyncResult.succeeded()) {
                deferredResult.setResult(asyncResult.result());
            } else {
                deferredResult.setErrorResult(asyncResult.cause());
            }
        });
return deferredResult;
}

private WorkerExecutor dbThreadPool = vertx.createSharedWorkerExecutor("DB", 16);

private Future<Result> getResultFromDB() {
    Future<Result> result = Future.future();
    dbThreadPool.executeBlocking(future -> {
            return 阻塞數據庫IO;
        }, false, asyncResult -> {
            if (asyncResult.succeeded()) {
                result.complete(asyncResult.result());
            } else {
                result.fail(asyncResult.cause());
            }
        });
    return result;
}

相當於通過另外的線程池(當然也可以通過原有線程池,反正就是要用和請求不一樣的線程,才能實現回調,而不是當次就阻塞等待),封裝了阻塞 JDBC IO。

但是,這樣幾乎對數據庫IO主導的應用性能沒有提升,還增加了線程切換,得不償失。所以,需要使用真正實現了 NIO 的數據庫客戶端。目前有這些 NIO 的 JDBC 客戶端,但是都不普及:

  1. Vert.x 客戶端:https://vertx.io/docs/vertx-jdbc-client/java/
  2. r2jdbc 客戶端:http://r2dbc.io/
  3. Jasync-sql 客戶端:https://github.com/jasync-sql/jasync-sql

每日一刷,輕松提升技術,斬獲各種offer:

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM