是時候考慮Spring非阻塞編程模式?R2DBC pk JDBC 和 WebFlux pk Web MVC 評測數據

導讀:Spring框架中,同時存在WebFlux和R2DBC這樣的響應式模塊,也存在Web MVC和JDBC這樣的阻塞框架。應該在什麼情況下使用不同技術棧,可能會困擾很多技術人。本文作者對這兩種技術棧做了詳細的對比和壓力測試,爲技術選型提供支持。

2017年9月發佈的Spring Framework 5中,引入了Spring WebFlux。WebFlux是完全響應式的技術棧。2019年12月發佈了Spring Data R2DBC,這是一個使用響應式的數據庫驅動。在本文中,我將證明在高併發下,WebFlux和R2DBC表現更好。該組合的響應時間和吞吐量都更好。並且在處理每個請求時使用更少的內存和CPU,而且你的Fat JAR會變得更小。在高併發的時候,使用WebFlux和R2DBC(如果你不需要JPA的話)是個好主意。

測試方法

本文中,我們嘗試瞭如下四種組合:

  • Spring Web MVC + JDBC 數據庫驅動

  • Spring Web MVC + R2DBC 數據庫驅動

  • Spring WebFlux + JDBC 數據庫驅動

  • Spring WebFlux + R2DBC 數據庫驅動

我已經將並行請求數以50個爲單位從4增加到500,分別爲負載生成器和服務分配4個核心(我的筆記本有12個核心)。我將所有的連接池都配置爲100。爲什麼要固定核數和連接池的大小?因爲在之前對JDBC和R2DBC的測試中,改變這些因素並沒有提供更多的數據,所以我決定在這個測試中保持固定的變量,以減少測試需要運行的時間。

我在服務上模擬一個GET請求。該服務從數據庫中獲取了10條記錄,並以JSON形式返回。首先,我對服務進行了2秒預熱。接下來,我開始了1分鐘的基準測試。我把每個場景運行5次(依次運行,而非5次之後再運行其他測試),並計算結果的平均值。我只統計了那些沒有錯誤的測試。當我將併發數增加到1000以上時,所有的實現都無一例外地有失敗。

我使用了Postgres(12.2)作爲數據庫。並且使用wrk來進行基準測試。我用下面的方法解析wrk的輸出。主要測量:

  • 響應時間—來自Wrk測試報告

  • 吞吐量(請求數)— 來自Wrk測試報告

  • 進程CPU的使用情況—用戶和內核時間(基於/proc/PID/Stat)

  • 內存使用量—私有和共享進程內存(基於/proc/PID/maps)

你可以在這裏查看所使用的測試腳本[1]。你可以在這裏查看所使用的代碼[2]。

測試結果

你可以在這裏查看我在圖表中使用的原始數據[3]。

響應時間

image

很顯然在高併發下,Spring Web MVC + JDBC可能不是你的最佳選擇。顯然在更高的併發下,R2DBC可以提供更好的響應時間。Spring Web MVC和Spring WebFlux也有類似的趨勢。

吞吐量

image

與響應時間類似,使用JDBC+Spring Web MVC在高併發下表現得更差。同樣的,R2DBC顯然表現的更勝一籌。如果你的後端仍然使用JDBC,那麼從Spring Web MVC轉移到Spring WebFlux也並不是一個好主意。在低併發時,Spring Web MVC + JDBC 表現最好。

CPU

image

CPU是指整個運行過程中的CPU時間,即進程用戶和內核時間之和。

使用JDBC+Web MVC的方案在高併發時消耗的CPU最高。JDBC+WebFlux的方案使用的CPU時間最少,但吞吐量也最低。當你查看平均每請求所使用的CPU時,你就可以衡量各種方式的CPU使用效率。

image

與JDBC相比,R2DBC平均每個請求使用的CPU更少。使用JDBC+WebFlux似乎不是一個好主意。JDBC+Web MVC在高併發量的情況下更加糟糕,而其他至少有一個非阻塞組件的實現更穩定。然而在低併發時,Web MVC + JDBC可以最有效地利用CPU。

內存

我們測量在運行結束時進程私有內存作爲內存消耗量。內存使用情況取決於垃圾回收。我們使用JDK 11.0.6和G1GC。Xms 設置爲 0.5 Gb (默認是我可用內存32 Gb 的 1/64)。Xmx 設置爲 8 Gb (默認是我可用內存 32 Gb 的 1/4)。

image

與Web MVC相比,WebFlux的內存使用量似乎更穩定,而WebMVC在高併發時的內存使用量更大。當使用WebFlux+R2DBC時,在高併發情況下,內存使用量最少。在低併發時,Web MVC + JDBC內存使用較低,但在高併發時,WebFlux + R2DB平均每個請求處理中使用的內存最少。

image

Fat Jar大小

下圖中JPA佔用了大頭。如果你使用R2DBC的情況下不使用它,那麼Fat JAR大小就會下降到15Mb左右!

image

總結

R2DBC+WebFlux是高併發時的好主意!

  • 在高併發時,使用R2DBC代替JDBC和使WebFlux代替Web MVC的好處顯而易見。

  • 處理單個請求所需的CPU更少。

  • 處理單個請求所需的內存更少。

  • 高併發時的響應時間更低。

  • 高併發時的吞吐量更好

  • Fat JAR較小(不使用JPA)。

  • 當只使用阻塞組件時,在高併發時,內存和CPU的使用效率會降低。

  • JDBC+WebFlux似乎不是一個好主意。R2DBC+Web MVC在高併發時比JDBC+Web MVC效果更好。

  • 你不需要使用完全無阻塞的堆棧來獲得使用R2DBC的優勢。但是,如果是使用Spring,最好將其與WebFlux結合起來。

  • 在低併發量(200個併發請求以下)時,使用Web MVC和JDBC可能會有更好的效果。通過測試來確定平衡點。

使用R2DBC時的一些挑戰

  • JPA無法處理像Spring Data R2DBC提供響應式功能。這意味着在使用R2DBC時,你將不得不手動做更多工作。

  • 還有其他響應式驅動,例如Quarkus Reactive Postgres客戶端(使用Vert.x)。他們不使用R2DBC,並且有不同的性能特性。

  • 有限的可用性

  • 不是所有的關係型數據庫都有響應式的驅動程序。例如,Oracle還沒有R2DBC實現。

  • 應用服務器仍然依賴於JDBC。在這個Kubernetes時代,人們還在使用那些上古功能嗎?

  • 當Java Fibers推出的時候(Project Loom,可能是Java 15),數據庫驅動的格局可能會再次發生變化,R2DBC可能不會成爲JDBC的繼任者。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章