終於可以愉快的擼Java非同步程式碼了!

白菜園發表於2021-01-18

  非同步響應式程式設計可以極大的提高系統的併發呑吐量,但由於Java沒有類似於其他語言的Async/Await機制,所以只能通過CompletableFuture.thenXXX()來串聯各個非同步任務,這給習慣了寫同步增刪改查的小夥伴們帶來了些小麻煩。如果說C#基於狀態機在編譯時實現了await轉換,那麼Java肯定也可以基於相同的原理實現await,作者上網一搜果然找到了ea-async,使用與C#相同的方式實現了await非同步方法,這回終於可以愉快的擼Java非同步程式碼了。

一、 示例程式碼

  舉個扣減庫存並儲存訂單的例子,在沒有await方式下的示例程式碼如下:

public CompletableFuture<?> saveOrder(Order order) {
    //1.開始事務
    return DataStore.DemoDB.beginTransaction().thenCompose(txn -> {
        //2.扣庫存
        var cmd = new SqlUpdateCommand<Order>();
        cmd.update(e -> e.Stock = e.Stock - order.Quantity); //當前量扣除訂單量
        cmd.where(e -> e.ProductId == order.ProductId);
        var outs = cmd.output(e -> e.Stock);
        return cmd.execAsync(txn).thenCompose(rows -> {
            //3.判斷庫存
            if (rows == 0 || outs.get(0) < 0) 
                return CompletableFuture.failedFuture("庫存不足");
            //4.儲存訂單
            return order.saveAsync(txn);
        }).thenCompose(r -> txn.commitAsync()); //5.遞交事務
    }).thenApply(r -> "Done.");
}

  WTF! 一層套一層的回撥,這還只是個簡單的例子啊。但是如果使用await的方式,業務邏輯就變得清爽多了:

import static sys.Async.await;

public CompletableFuture<?> saveOrder(Order order) {
    //1.開始事務
    var txn = await(DataStore.DemoDB.beginTransaction());
    //2.扣庫存
    var cmd = new SqlUpdateCommand<Order>();
    cmd.update(e -> e.Stock = e.Stock - order.Quantity); //當前量扣除訂單量
    cmd.where(e -> e.ProductId == order.ProductId);
    var outs = cmd.output(e -> e.Stock); 
    var rows = await(cmd.execAsync(txn));
    //3.判斷庫存
    if (rows == 0 || outs.get(0) < 0)
        return CompletableFuture.failedFuture("庫存不足");
    //4.儲存訂單
    await(order.saveAsync(txn));
    //5.遞交事務
    await(txn.commitAsync());
    return CompletableFuture.completedFuture("Done.");
}

二、 實現原理

  核心思想沒有變,還是程式碼分析轉換。作者原本想利用JDT在原始碼層進行await轉換,但是工作量還是比較大的,在這裡感謝github/ea-async專案,使得作者只修改了十幾行程式碼就實現了上述效果,具體轉換過程如下:

  1. 服務模型的虛擬程式碼經過JDT分析、轉換、編譯為class檔案(參考前篇說明);
  2. 呼叫ea-async的Transformer類直接分析與轉換class檔案內的await方法呼叫,生成新的class檔案。

具體參考原始碼PublishService的compileService()及transformAsync()方法。

三、 本篇小結

  先解釋一下上篇"優雅的Java ORM"小夥伴們的評論,有說這麼多ORM幹嗎還要造一個的,也有說不夠"優雅"的,其實造輪子最根本的原因是:非同步支援!,Spring WebFlux及VertX有相應的非同步運算元據庫的支援,但本框架沒法整合。MyBatis之類的全是同步操作,小的應用系統沒有問題,高併發系統的呑吐量就非常可憐了,這個大家可以測一下Spring MVC與WebFlux的效能對比。

  軟體技術日新月異,新一代的開發框架必定是非同步的!對於作者來講Spring還不夠簡單優雅,執行的也不夠快,所以才會嘗試重新造這個大輪子,爭取讓應用系統的開發人員有更多的時間關注資料結構、業務邏輯和使用者體驗,也爭取讓小夥伴們有一個絲般順滑的開發體驗。

邊碼程式碼邊碼文實屬不易,作者需要您的支援請您多多點贊推薦!另歡迎感興趣的小夥伴加入我們!

相關文章