誰才是微服務贏家:Quarkus 與 Spring Boot
在容器時代(“Docker 時代”)Java 仍然處于領先地位,但哪個更好?Spring Boot 還是 Quarkus?
誰會最先進的?Spring Boot 或 Quarkus。
在容器時代(“ Docker 時代”),無論您是否在使用它,都不可否定java的活力。Java 在性能方面一直比較有優勢,主要是因為代碼和真實機器之間的抽象層,多平臺的成本(一次編寫,隨處運行 - 還記得嗎?),其中包含 JVM -between(JVM:模擬真實機器所做的軟件機器)。
如今,使用微服務架構,也沒有任何優勢,為總是在同一個地方和平臺上運行的東西(Docker 容器 - Linux) 環境構建多平臺(解釋)的東西。可移植性現在不那么重要了(可能比以往任何時候都重要),那些額外的抽象級別并不重要。
話雖如此,讓我們對在Java中生成微服務的兩種替代方案進行簡單而原始的比較:非常知名的Spring Boot和不太知名的(尚未)Quarkus。
反對者
Quarkus是什么?
一套適用于GraalVM和 HotSpot的開源技術 ,用于編寫 Java 應用程序。它提供(承諾)超快的啟動時間和更低的內存占用。這使其成為容器和無服務器工作負載的理想選擇。它使用 Eclipse 微配置文件(JAX-RS、CDI、JSON-P),這是 Java EE 的一個子集來構建微服務。
GraalVM 是一個通用的多語言虛擬機(JavaScript、Python、Ruby、R、Java、Scala、Kotlin)。 GraalVM (特別是 Substrate VM)使提前(AOT)編譯成為可能,將字節碼轉換為本地機器碼,從而生成可以本地執行的二進制文件。
請記住,并非所有功能都可以在本機執行中使用,AOT 編譯有其局限性。注意這句話(引用 GraalVM 團隊):
我們運行需要一個封閉世界假設的激進靜態分析,這意味著在運行時可訪問的所有類和所有字節碼必須在構建時已知。
因此,例如,反射和 Java 本機接口 (JNI) 將不起作用,至少是開箱即用的(需要一些額外的工作)。您可以在本機圖像 Java 限制文檔中找到限制列表。
Spring Boot是什么?
這是真的嗎?好吧,我只想說一句(請隨意跳過),一句話:Spring Boot構建在 Spring Framework 事實上,是一個開源框架,它提供了一種更簡單的方式來構建、配置和運行基于 Web 的 Java 應用程序. 使其成為微服務的良好候選者。
戰斗準備——創建 Docker 鏡像
Quarkus鏡像
讓我們創建 Quarkus 應用程序,以便稍后將其包裝在 Docker 映像中。基本上,我們將做與 Quarkus入門教程相同的事情。
使用 Quarkus maven 原型創建項目:
mvn io.quarkus:quarkus-maven-plugin:1.0.0.CR2:create
-DprojectGroupId=ujr.combat.quarkus
-DprojectArtifactId=quarkus-echo
-DclassName="ujr.combat.quarkus.EchoResource"
-Dpath="/echo"
這將導致我們項目的結構,如下所示:
請注意,還創建了兩個示例Dockerfile (src/main/docker):一個用于普通JVM App Image,另一個用于Native App Image。
在生成的代碼中,我們只需要更改一件事,添加下面的依賴項,因為我們要生成 JSON 內容。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
Quarkus 在整個 RESTEasy 項目實現中使用 JAX-RS 規范。
這是我們的“整個”應用程序:
這就是全部,使用下一個命令我們可以看到應用程序正在運行:
mvn clean compile quarkus:dev
在這種模式下,我們也開啟了熱部署,后臺編譯。讓我們做一個簡單的測試來看看:
curl -sw "\n\n" http://localhost:8080/echo/ualter | jq .
現在我們看到它正在工作,讓我們創建 Docker 映像。從這里下載 GraalVM:https://github.com/graalvm/graalvm-ce-builds/releases 。
重要的! 不要下載最新版本 19.3.0,它與Quarkus 1.0不兼容,也許 Quarkus 1.1 會。現在應該工作的版本是 GraalVM 19.2.1,得到這個。
配置其環境變量的主路徑:
## At macOS will be: export
GRAALVM_HOME=/Users/ualter/Developer/quarkus/graalvm-ce-java8-19.2.1/Contents/Home/
然后在你的環境中安裝 GraalVM 的 Native Image:
$GRAALVM_HOME/bin/gu install native-image
讓我們為當前平臺生成本機版本(在這種情況下,將為 macOS 生成本機可執行文件)。
mvn package -Pnative
quarkus-echo-1.0-SNAPSHOT-runner如果一切正常,我們可以在 ./target 文件夾中找到一個名為的文件。這是您的應用程序的可執行二進制文件,您只需運行以下命令即可啟動它
./target/quarkus-echo-1.0-SNAPSHOT-runner:無需使用JVM(普通:java -cp app:lib/:etc App.jar*),它是一個本機可執行二進制文件。
讓我們為我們的應用程序生成一個 Native Docker Image。該命令將創建一個 Native 鏡像,即帶有 Linux 原生可執行應用程序的 Docker 鏡像。默認情況下,本機可執行的文件是基于當前平臺 (macOS) 創建的,因為我們知道生成的可執行文件與容器 (Linux) 的平臺不同,我們將指示 Maven 構建從在容器內,生成原生 docker 鏡像:
mvn package -Pnative -Dquarkus.native.container-build=true
此時,一定要有一個Docker容器運行時,一個工作環境。
該文件將是一個 64 位 Linux 可執行文件,因此很自然,這個二進制文件無法在我們的 macOS 上運行,它是為我們的 docker 容器映像構建的。所以,繼續前進......讓我們去生成 docker 圖像......
docker build -t ujr/quarkus-echo -f src/main/docker/Dockerfile.native .
## Testing it...
docker run -i --name quarkus-echo --rm -p 8081:8081 ujr/quarkus-echo
附帶說明,關于 Docker 映像大小:
最終的 docker 鏡像是115MB,但是你可以使用distroless 鏡像版本來制作一個很小的 Docker 鏡像。Distroless 映像僅包含您的應用程序及其運行時依賴項,其他所有內容(包管理器、shell 或標準 Linux 發行版中常見的普通程序)都將被刪除。我們應用程序的 Distroless 映像大小為42.3MB。該文件
./src/main/docker/Dockerfile.native-distroless有生成它的收據。
關于 Distroless Images: “*將運行時容器中的內容嚴格限制為應用程序所需的內容是Google和其他在生產環境中使用容器多年的科技巨頭采用的最佳實踐*”
spring boot鏡像
到此,想必大家都知道如何制作一個普通的Spring Boot Docker鏡像了,我們就略過細節吧?只是一個重要的觀察,代碼是完全相同的。更好的說法是,幾乎相同,因為我們使用的是 Spring 框架注解,當然。這是唯一的區別。您可以檢查提供的源代碼中的每個細節(下面的鏈接)。
mvn install dockerfile:build
## Testing it...
docker run --name springboot-echo --rm -p 8082:8082 ujr/springboot-echo
戰爭
讓我們啟動兩個容器,讓它們啟動并運行幾次,然后比較Startup Time和Memory Footprint。
在這個過程中,每個容器都被創建和銷毀 10 次。后來,分析了它們的啟動時間及其內存占用。下面顯示的數字是基于所有這些測試的平均結果。
啟動時間
顯然,當涉及到可擴展性和無服務器架構時,這方面可能會發揮重要作用。
關于 Serverless 架構,在此模型中,通常一個臨時容器將由事件觸發以執行任務/功能。在云環境中,價格通常基于執行次數,而不是之前購買的一些計算容量。因此,這里的 冷啟動可能會影響這種類型的解決方案,因為容器(通常)只會在執行其任務時才處于活動狀態。
在可擴展性中,很明顯,如果需要突然向外擴展,啟動時間將定義容器完全準備好(啟動并運行)以響應呈現的加載場景所需的時間。
場景有多突然(需要和快速),長時間冷啟動的情況可能更糟。
讓我們看看它們在啟動時間方面的表現:
好吧,您可能已經注意到它是在啟動時間圖中插入的另一個測試選項。實際上,它與 Quarkus 應用程序完全相同,但使用 JVM Docker 映像(使用 Dockerfile.jvm)生成。正如我們所看到的,即使是使用 Docker Image 和 JVM Quarkus 應用程序的應用程序也比 Spring Boot 具有更快的啟動時間。
毋庸置疑,Quarkus Native 應用程序顯然是贏家,它是迄今為止啟動速度最快的應用程序。
內存占用
現在,讓我們檢查一下內存的情況。檢查每個容器應用程序在啟動時需要消耗多少內存,以使自己啟動并運行,準備好接收請求。
結論
總之,這就是我們在Linux Ubuntu中看到的結果:
看起來 Quarkus 贏得了這兩輪比賽(啟動時間和內存足跡),以明顯的優勢戰勝了對手 SpringBoot。
這可能會讓我們感到疑惑……也許是時候考慮一些真正的測試、經驗,并嘗試使用 Quarkus。我們應該看看它在現實生活中的表現如何,它如何適合我們的業務場景,以及最有用的地方。
但是,我們不要忘記缺點,正如我們在上面看到的,JVM 的某些功能在本機可執行二進制文件中(還/很容易)無法工作。無論如何,也許是時候給 Quarkus 一個證明自己的機會了,特別是如果冷啟動問題一直困擾著你。在環境中使用一兩個配備 Quarkus 的 Pod (K8s) 怎么樣,一段時間后看看它的表現會很有趣,不是嗎?