前言
Java微服務能像Go微服務一樣快嗎?
這是我最近一直在思索地一個問題。
去年8月份的the Oracle Groundbreakers Tour 2020 LATAM大會上,Mark Nelson和Peter Nagy就對此做過一系列基礎的的測試用以比較。接下來就給大家介紹下。
在程式設計師圈子裡,普遍的看法是Java老、慢、無聊 ,而Go是快、新、酷
為了儘可能的進行一個相對公平的測試,他們使用了一個非常簡單的微服務,沒有外部依賴關係(比如資料庫),程式碼路徑非常短(只是操縱字串),使用了小型的、輕量級的框架(Helidon for Java和Go工具包for Go),試驗了不同版本的Java和不同的jvm。
對決雙雄
我們先來看下擂臺兩邊的選手:
- 身穿深色戰服的選手是JAVA
Java是由被甲骨文收購的Sun Microsystems開發的。它的1.0版本是1996年釋出的,最新的版本是2020年的Java15。主要的設計目標是Java虛擬機器和位元組碼的可移植性,以及帶有垃圾收集的記憶體管理。它是全世界最流行的語言之一,在開源環境下開發。
我們先看下JAVA的問題,大家普遍認為它最大的問題就是速度慢,已經慢到讓人覺得不再是合理的,而是更具歷史意義的。不過這麼多年來,Java誕生了很多不同的垃圾收集演算法用來加快它執行的速度。
Oracle實驗室最近已經開發了一個新的Java虛擬機器GraalVM,它有一個新的編譯器和一些令人興奮的新特性,比如能夠將Java位元組碼轉換成一個本機映像,可以在沒有javavm的情況下執行等。
- 而它的對手就是年輕充滿活力的GO
GO是由谷歌的羅伯特·格里默、羅伯·派克和肯·湯姆森建立的。他們對UNIX、B、C、Plan9、UNIX視窗系統等做出了重大貢獻。GO是開源的,在2012年釋出了1.0版本(比JAVA晚了16年),在2020年釋出了1.15版本。無論是在採用方面,還是在語言和工具生態系統本身方面,它都在快速增長。
GO受C、Python、JavaScript和C++等多種語言的影響。被設計成高效能網路和多處理的最佳語言。
StackOverflow有27872個帶“Go”的問題,而Java只有1702730個。足見長江後浪推前浪。
Go是一種靜態型別的編譯語言。它有稱為goroutines的輕量級程式(這些不是OS執行緒),它們之間有獨特的通訊通道(型別化的,FIFO)。Go是許多CNCF專案的首選語言,例如Kubernetes、Istio、Prometheus和Grafana
賽前對比
從個人感覺來說,Go相比JAVA來說,優點在於:
- Go更容易實現複合、純函式、不變狀態等功能模式。
- Go處於生命週期的早期,因此它沒有向後相容性的沉重負擔—Go仍然可以輕易打破某些限制來改進。
- Go編譯成一個本機靜態連結的二進位制檔案-沒有虛擬機器層-二進位制檔案擁有執行程式所需的一切,這對於“從頭開始”的容器來說非常好。
- Go體積小、啟動快、執行快(目前是的)
- Go沒有OOP,繼承,泛型,斷言,指標演算法
- Go寫法上較少的括號
- Go沒有迴圈依賴、沒有未使用的變數或匯入、沒有隱式型別轉換的強制
- Go樣板程式碼少得多
缺點是:
- Go工具生態系統還不成熟,尤其是依賴關係管理——有幾個選項,沒有一個是完美的,特別是對於非開源開發;仍然存在相容性挑戰。
- 構建具有新的/更新的依賴項的程式碼非常慢(比如Maven著名的“下載Internet”問題)
- 匯入將程式碼繫結到儲存庫,這使得在儲存庫中移動程式碼成為一場噩夢。
- 除錯、評測等仍然是一個挑戰
- 用到了指標
- 需要實現一些基本的演算法
- 沒有動態連結
- 沒有太多旋鈕來調優執行或垃圾收集、概要檔案執行或優化演算法。
比賽開始
使用JMeter來執行負載測試。這些測試多次呼叫這些服務,並收集有關響應時間、吞吐量(每秒事務數)和記憶體使用情況的資料。對於Go,收集駐留集大小;對於Java,跟蹤本機記憶體。
在測量之前,使用1000次服務呼叫對應用程式進行預熱。
應用程式本身的原始碼以及負載測試的定義都在這個GitHub儲存庫中:https://github.com/markxnelson/go-java-go
第一回合
在第一輪測試中,在一臺“小型”機器上進行了測試,是一臺2.5GHz雙核Intel core i7膝上型電腦,16GB記憶體執行macOS。測試執行了100個執行緒,每個執行緒有10000個迴圈,上升時間為10秒。Java應用程式執行在JDK11和Helidon2.0.1上。使用Go 1.13.3編譯的Go應用程式。
結果如下:
可以看出,第一回合是Go贏了!
JAVA佔的記憶體太多了;預熱對JVM有很大的影響—我們知道JVM在執行時會進行優化,所以這是有意義的
在第一回合的基礎上,意猶未盡的又引入GraalVM映像以使 Java 應用程式的執行環境更接近於 Go 應用程式的環境,新增了 GraalVM 映像測試(用 GraalVM EE 20.1.1ー JDK 11構建的本機映像)的結果是:
通過使用 GraalVM 映像在 JVM 上執行應用程式,我們沒有看到吞吐量或響應時間方面的任何實質性改進,但是記憶體佔用的確變小了。
下面是一些測試的響應時間圖:
第二回合
在第二輪測試中,使用一臺更大的機器上執行測試。36核(每個核兩個執行緒)、256GB記憶體、執行oraclelinux7.8的機器。
和第一輪類似,使用了100個執行緒,每個執行緒使用了10,000個迴圈,10秒的加速時間,以及相同版本的 Go,Java,Helidon 和 GraalVM。
結果如下:
這一回合是GraalVM 映像贏了!
下面是一些測試的響應時間圖:
在這個測試中,Java變體的表現要好得多,並且在沒有使用Java日誌記錄的情況下,它的效能大大超過了Go。Java似乎更能使用硬體提供的多核和執行執行緒(與Go相比)。
這一輪的最佳表現來自GraalVM native image,平均響應時間為0.25毫秒,每秒事務數為82426個,而Go的最佳結果為1.59毫秒和39227個tps,然而這是以多佔用兩個數量級的記憶體為代價的!
GraalVM映像比在jvm上執行的同一應用程式快大約30–40%!
第三回合
這次,比賽在Kubernetes叢集中執行這些應用程式,這是一個更自然的微服務執行時環境。
這次使用了一個Kubernetes 1.16.8叢集,它有三個工作節點,每個節點有兩個核心(每個核心有兩個執行執行緒)、14GB的RAM和oraclelinux7.8。
應用程式訪問是通過Traefik入口控制器進行的,JMeter在Kubernetes叢集外執行,用於一些測試,而對於其他測試,使用ClusterIP並在叢集中執行JMeter。
與前面的測試一樣,我們使用了100個執行緒,每個執行緒使用了10,000個迴圈,以及10秒的加速時間。
下面是各種不同容器的大小:
- Go 11.6MB 11.6 MB
- Java/Helidon 1.41GB 1.41 GB
- Java/Helidon JLinked 150MB 150mb
- Native image 25.2MB 25.2 MB
結果如下:
下面是一些測試的響應時間圖:
在這一輪中,我們觀察到 Go 有時更快,GraalVM 映像有時更快,但這兩者之間的差別很小(通常小於5%)。
Java似乎比Go更善於使用所有可用的核心/執行緒—我們在Java測試中看到了更好的CPU利用率。Java效能在擁有更多核心和記憶體的機器上更好,Go效能在較小/功能較弱的機器上更好。在一臺“生產規模”的機器上,Java很容易就和Go一樣快,或者更快
最後
接下來會做更多的測試比賽,來看一看究竟誰更好!有興趣的你也可以自己試一試,記得告訴我們結果哦!
本文參考:https://medium.com/helidon/can-java-microservices-be-as-fast-as-go-5ceb9a45d673
歡迎關注我的公眾號:程式猿DD,獲得獨家整理的免費學習資源助力你的Java學習之路!另每週贈書不停哦~