云原生时代,Java还是Go?
Java曾經著名的座右銘:"一次編寫,到處運行",已經很過時了,因為現在我們只想在容器里運行代碼。在容器里,一個 "Just in time "的編譯器意義不大。
出于這個原因,可能為了更好地適應云計算,Java生態系統正處于轉型之中。Oracle 的GraalVm允許將字節碼編譯成Linux可執行文件(ELF),而Rad Heat的Quarkus以及其他框架,則立志讓響應式服務這件事變得更簡單。Quarkus以Netty和Vertx.x為核心,可以用來構建非常高效的響應式Web服務。
Java編譯成可執行二進制文件,以毫秒級的速度啟動,內存占用很小。這樣就可以利用Java生態系統,甚至可以用其他JVM語言(如Scala和Kotlin)編寫。你可以用online項目生成器玩玩Quarkus,或者用maven插件在本地生成一個項目。
而Golang則是為云而生的,在容器中運行時,沒有遺留負擔。它被認為是云端的編程語言。生成的二進制可執行文件很小,快速啟動,內存占用也很小,而且這是從Go誕生之初就具備的特性。Golang的流行對 Java 世界形成了嚴峻的挑戰。
Java有機會嗎,也許只有時間才會告訴我們最終答案。然而,出于好奇,我想從性能和開發體驗方面比較一下 Java 和 Golang 的云原生服務。
在這篇文章中,我將使用兩種語言來寫同樣的服務。比較它們的CPU使用率、RAM、延遲和運行速度。這些服務將在容器中啟動,資源分配相同,使用ab來測試。
對于我的案例來說,這是一個 "足夠好 "的基準,因為我不假設找到最好/最差的基準結果,而是在同一環境下執行運行兩個基準測試進行比較。
場景
這兩個服務將連接到在另一個容器中運行的MySQL數據庫,有一個表和三行數據。
每一個服務都會獲取所有記錄,將它們轉化為對象,然后輸出JSON數組。
ab將發出10K請求,并發級別為100,quarkus JVM版本運行兩次(用于測試 "冷"/"暖 "JVM)。
Go語言版本
Go語言版本使用gin框架。
# the service package mainimport ("database/sql""fmt""github.com/gin-gonic/gin"_ "github.com/go-sql-driver/mysql""net/http" )type Fruit struct {Id int `json:"id"`Name string `json:"name"` }var con *sql.DBfunc init(){//opening a mysql connection pool with another containerdb, err := sql.Open("mysql", "root:password@tcp(host.docker.internal:3306)/payments")if err != nil {panic("failed to open a mysql connection")}con = db }func main() {r := gin.Default()r.GET("/fruits", fruits)r.Run() //server up on 8080 }// THE REQUEST HANDLER func fruits(c *gin.Context) {fruits := getFruits()c.JSON(http.StatusOK, fruits) }func getFruits() []Fruit {rows, _ := con.Query("SELECT * FROM fruits")fruits := []Fruit{}for rows.Next() {var r Fruitrows.Scan(&r.Id, &r.Name)fruits = append(fruits, r)}return fruits }Golang的MySQL驅動的使用go-sql-driver。golang的代碼風格是非常明確的。一種一切都在眼前態度。主函數啟動服務器,配置請求處理程序,打開DB連接。
編譯本地可執行文件
Kotlin版本
數據庫連接使用Quarkus React Mysql 擴展。
與Go版本相比,代碼有很大不同,比如CDI依賴注入,使用javax注釋的聲明式路由,自動配置解析,以及數據源/連接創建/服務器引導。這是使用框架的代價,它為你完成了繁重的工作,并決定了做事方式。不過,它比go版本代碼要簡短很多。
這里使用Netty響應式web服務器,由Vert.x多事件循環包裝,還有一個Vert.x響應式MySQL驅動,這樣可以用一個線程處理多個DB連接。
另外,我可以使用Kotlin的集合庫的fold函數,這種函數還沒有通用的Go版本。
編譯Java版本的可執行文件
我已經弄清楚構建過程中發生了什么,其核心是SubstrateVM。它被設計在AOT過程中的可嵌入虛擬機,它會鏈接到我們的代碼,并作為一個單元進行編譯。然而根據Oracle的說法,SubstrateVM的優化比HotSpot Vm少,垃圾收集器也比較簡單。
該AOT編譯器被稱為 "Graal",它是語言不相關的。java字節碼需要被翻譯成一種中間表示法(Truffle語言)。這在這篇文章【1】中可以找到關于Graal和Truffle的相關論述。
構建一個 Java 本地可執行文件看起來更復雜,編譯得更慢,它產生的二進制文件幾乎是Go版本兩倍大小。然而一個35M的可執行二進制文件和Java FatJar相比,還是小D多了。35MB甚至可以讓你使用aws lambda運行。
壓力測試
我在本機運行所有測試,設置如下。
-
MacBook Pro(15英寸,2017年
-
2.9 GHz英特爾酷睿i7(8個核心)。
-
16 GB 2133 MHz LPDDR3
使用cAdvisor的工具來監控容器的統計數據。
場景
- Quarkus JVM?hotspot
- Quarkus Java native?
- Golang
上述的每種情況都在以下三種配置上測試
-
100MB / 0.5 CPU | 200MB / 1 CPU | 300MB / 2 CPU
我主要關注:
-
cpu/ram利用率(多核的利用率)
-
cpu/ram峰值
-
cpu/ram空余
-
啟動時間
-
響應延遲avg/max
-
吞吐量(每秒請求數)
測試結果
看起來Quarkus已經為生產環境做好準備了,它允許簡單的JVM/原生發布/開發 模式,并允許在本地運行原生測試。只要你不使用反射或JNI,根據GraalVM的配置就是可行的。否則,你將不得不自己配置graal編譯器,然而現在也有解決方案。
延遲和吞吐量
Golang 和原生 Java 的測試結果比較接近,雖然平均來說 Golang 版本的測試結果略好一些。不過,Java Native版本的測試結果更穩定。Golang服務有時在1.25μs內完成響應,也有一部分需要7s才能完成。
"預熱 "后的JVM版本結果也不差,但比Native或Go版本稍遜一籌。
CPU利用率
使用0.5核的時候,Go和native-java在負載下似乎都表現不佳,而用2核啟動時,也沒有明顯改善。這可能是因為工作負載的瓶頸是IO。或者是因為gin/Netty的默認配置沒有考慮到多核的問題。
而JVM版本則利用了所有給定的核心,并在各個維度上提升了性能。
內存使用率
在壓力下,Java native 使用40MB,Golang 使用24MB。兩種情況下都還不錯,雖然Golang版本使用的內存幾乎少了一倍。
JVM使用了140MB。和Quarkus官方的統計完全一樣。對于JVM來說還不錯,但比Golang版本多了近6倍。
啟動時間
Golang和cloud-native java都能立即啟動,然而JVM版本需要幾秒鐘(取決于分配的CPU),并且在啟動時產生CPU峰值。如果配置不當,會導致k8s HPA發飆,并增加pods。
開發體驗
這與其說是一個實際問題,不如說是一個宗教問題。Quarkus 使用了在 Java 世界中很常見的抽象(比如基于注解的DI)。它為你啟動服務并創建連接池。它可以使用豐富的集合標準庫和generics。然而,這可能感覺有點像黑魔法,一旦有些組件不工作,你會感覺很無助。此外,將 Java 代碼編譯成原生二進制并不是那么簡單,有一些限制和注意事項是你必須知道的,并非每個Java庫都能兼容原生編譯。一旦使用一個不兼容的庫(比如Guice),你就需要自己配置Graal VM。
Quarkus 和 Graal VM "相對 "較新。所以可能會有一些問題。但由于雙模式(JVM或原生)。在原生版本的某些組件停止工作的情況下,總是有一個后備方案,這對任何新問題來說都是很好的變通方法。
另一方面,Golang 在成立10年后才承認它需要generics。而且它肯定不喜歡框架使用很多魔法操作。這在很多方面既是好事也是壞事。此外,盡管 Go 社區做的非常好,然而可用的工具和庫還是相對較少。然而它的編譯和構建過程更快/更簡單。而且兼容每個Golang的包,沒有java-native平臺帶來的限制。
結論
Java已經為云原生做好了準備,Golang并沒有大幅度領先。相信未來Cloud Native Java會被大規模使用。
原文地址:https://medium.com/swlh/cloud-native-java-vs-golang-2a72c0531b05
譯者注:翻譯過程去掉了中間關于測試過程記錄,因為測試結果在表格中已經記錄了,測試的細節過于冗長。
總結
以上是生活随笔為你收集整理的云原生时代,Java还是Go?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL 也替换了master、sla
- 下一篇: “算法复杂度”——其实并没有那么复杂