終於,Spring 對 JVM 動手了

終於,Spring 對 JVM 動手了

Spring 在今年 3 月份推出了 Spring Native Beta 版本,我本來還想著等正式釋出了再研究下,不用等了,現在我們就來嚐嚐鮮。

https://spring。io/blog/2021/03/11/announcing-spring-native-beta

Spring Native 簡介

我們都知道,傳統的 Spring 應用程式都是必須依賴於 Java 虛擬機器(JVM)執行的,Spring Native 的誕生

就是無需 JVM

,它提供了另外一種執行和部署 Spring 應用的方式(目前只支援 Java 和 Kotlin),

透過 GraalVM 將 Spring 應用程式編譯成原生映象。

Spring Native 特點

1、

無需 JVM 環境,

Spring Native 應用程式可以作為一個可執行檔案獨立部署;

2、應用即時啟動,一般情況下應用啟動時間 < 100ms;

3、即時的峰值效能;

4、更少的記憶體消耗;

Spring Native 缺點

Spring Native 應用啟動那麼快也是有代價的,和 JVM 應用相比:

1、構建更笨重、構建時間更長;

2、更少的執行時最佳化;

3、很多 Java 功能受限;

4、很多特性還很不成熟;

Spring Native 應用場景

1、Spring Cloud 無伺服器化(Serverless);

2、以更廉價持久的方式執行 Spring 微服務;

3、非常適合 Kubernetes 平臺,如:VMware Tanzu;

4、為 Spring 應用建立更佳的容器映象;

Spring Native 和 JVM 的區別

1、Spring Native 構建時會進行應用程式靜態分析;

2、Spring Native 構建時會移除未被使用的元件;

3、Spring Native 反射、資源、動態代理需要配置化;

4、Spring Native 構建時的 classpath 是固定不變的;

5、Spring Native 沒有類延遲載入,可執行檔案包含所有內容都在啟動時載入到記憶體;

6、Spring Native 構建時會執行一些程式碼;

7、Spring Native 對於 Java 應用程式還存在一些侷限性;

GraalVM 簡介

Spring Native 的核心就是 Oracle 的黑科技: GraalVM。

GraalVM 是一個由 Oracle 開發的全棧通用虛擬機器,擁有高效能、跨語言互動等逆天特性,不僅支援了 Java、Scala、Groovy、Kotlin 等基於 JVM 的語言,以及 C、C++ 等基於 LLVM 的語言,還支援其他像 JavaScript、Ruby、Python 和 R 語言等,可提高多種語言的執行速度和吞吐量。

終於,Spring 對 JVM 動手了

GraalVM 有以下幾個特性。

更加高效快速的執行程式碼

能與大多數程式語言直接互動

使用 Graal SDK 嵌入多語言

建立預編譯的原生映象

提供一系列工具來監視、除錯和配置所有程式碼

具體就不介紹了,閱讀我之前分享的這篇文章:Oracle 釋出了一個全棧虛擬機器 GraalVM

重點來看原生映象功能:

$ javac HelloWorld。java$ time java HelloWorlduser 0。070s$ native-image HelloWorld$ time 。/helloworlduser 0。005s

GraalVM 可以預編譯成原生映象,從而極大提速了啟動時間,並能減少 JVM 應用的記憶體佔用。現在你知道為什麼 Spring Native 啟動那麼快的原因了!

Spring Native 正是透過 GraalVM 提供了對傳統 Spring 應用程式的輕量級執行方式,在不用修改任何傳統應用程式程式碼的情況下,透過整合 Spring Native 專案就能輕鬆實現。

開始嚐鮮

構建 Spring Native 應用的兩種方式:

1、使用

Spring Boot Buildpacks

來生成一個包含原生可執行檔案的輕量級容器;

2、使用

GraalVM native image Maven

外掛來生成一個包含原生可執行檔案;

本文使用第一種方式進行嚐鮮!

1、環境要求

這種方式需要安裝 Docker 環境:

Linux 需要配置非 root 使用者可執行

Mac 需要配置最大記憶體為 8G 或以上

因為我本地已經裝好了,這裡不再演示了,不會的點選這裡閱讀參考一下,或者關注:Java技術棧,在歷史文章中搜索閱讀。

2、新增依賴

Spring Native 在 start。spring。io 上面已經可以開始使用了,在頁面上新增一個 “

Spring Native

“ 依賴進去就好,如下所示:

終於,Spring 對 JVM 動手了

Spring Boot:

org。springframework。boot spring-boot-starter-parent 2。4。5

Spring Native:

org。springframework。experimental spring-native ${spring-native。version}

注意依賴版本:

Spring Native 最新版本為:0。9。2,只支援 Spring Boot 2。4。5

3、新增 Spring AOT 外掛

新增 Spring AOT 外掛:

org。springframework。experimental spring-aot-maven-plugin 0。9。2 test-generate test-generate generate generate

Spring AOT 外掛執行所需的提前轉換,以提升原生映象的相容性。

4、開啟原生映象支援

在 Spring Boot Maven 外掛中增加以下配置:

org。springframework。boot spring-boot-maven-plugin paketobuildpacks/builder:tiny true

5、新增 Maven 倉庫支援

Spring Native 依賴和外掛需要在 Spring 倉庫中下載,需要新增以下配置。

spring-release Spring release https://repo。spring。io/release spring-release Spring release https://repo。spring。io/release

如果不能正常下載 Native 依賴和外掛,需要檢查 Maven 的 settings。xml 檔案:

nexus-aliyun *,!spring-release Nexus aliyun http://maven。aliyun。com/nexus/content/groups/public

把 mirrorOf 值由

修改為:

,!spring-release

6、新增測試介面

新增一個測試介面,原生應用啟動後,方便測試下可行性。

/** * 來源:Java技術棧 */@SpringBootApplication@RestControllerpublic class Application { public static void main(String[] args) { SpringApplication。run(Application。class); } @RequestMapping(“/native/hi”) @ResponseBody public String hiNative() { return “hi native application。。。”; }}

本文所有程式碼已上傳至:https://github。com/javastacks/spring-boot-best-practice

7、構建原生應用

Maven 外掛構建命令:

mvn spring-boot:build-image

這個會建立一個 Linux 容器,使用 GraalVM 原生映象編譯器構建出原生應用程式,容器映象預設只安裝在本地。

在 IDEA 外掛中執行:

終於,Spring 對 JVM 動手了

配置好後開始構建:

終於,Spring 對 JVM 動手了

會看到大量這樣的錯誤,不用理會,這個會在未來移除。

終於,Spring 對 JVM 動手了

最終構建完成,一個簡單的 Spring Boot 應用程式,這個構建卻過程花了我 4 分鐘。。

8、執行原生應用

使用平常執行 Docker 映象的方式就能執行原生應用:

docker run –rm -p 8080:8080

當然也可以在專案中編寫

docker-compose。yml

檔案的方式,這裡不再演示,感興趣的可以關注Java技術棧,在歷史文章中搜索閱讀 Docker 系列文章。

一般情況下,執行原生應用程式只需要 100 毫秒以下,而執行基於 JVM 的應用程式大概需要 15 秒左右。

事實是否如此呢,一起來看看!

終於,Spring 對 JVM 動手了

我天,82 毫秒就啟動了,啟動確實快。

再來訪問我們之前寫的介面:

http://localhost:8080/native/hi

終於,Spring 對 JVM 動手了

輸出正常,原生應用驗證完成。

另外,在 target 目錄中也生成了可執行的 jar 包:

終於,Spring 對 JVM 動手了

然後我們用傳統 JVM 環境來執行下:

java -jar spring-boot-native-1。0。jar

終於,Spring 對 JVM 動手了

啟動時間:1。903 秒,雖然看起來差距不大,但原生應用啟動時間(0。082 秒)也比 JVM 快了 23 倍,在不同的程式碼量面前可能會有較大差距的體現。

當然這只是我測試的參考時間,但可以說明的原生應用執行確實要比 JVM 快不少!

我們再來比對下包的大小

檢視剛生成的 Docker 映象:

docker image ls

終於,Spring 對 JVM 動手了

檢視基於 JVM 的可執行 jar 包:

終於,Spring 對 JVM 動手了

Docker 映象大小:80。7 M,而基於 JVM 執行的可執行 jar 包卻只有不到 20M。

這是因為原生映象不僅包含了應用程式中所使用到的來自 JDK、Spring 中的必須項,還包含了一個最小化的 OS 系統層,所以肯定是要比之前的要大不少。

總結

本文介紹了 Spring Native 的特點,及演示了基於 Docker 映象的原生應用。

本文所有演示程式碼已上傳至:

https://github。com/javastacks/spring-boot-best-practice

感興趣的都可以 Star 下該倉庫,包含了之前寫的 Spring Boot 教程及示例原始碼。

當然除了基於 Docker 映象,還可以使用原生映象 Maven 外掛的方式,那種方式不需要 Docker,但需要安裝原生映象編譯器 GraalVM,道理是一樣的,這裡就不再演示了,有興趣的可以參考:

https://docs。spring。io/spring-native/docs/current/reference/htmlsingle/#getting-started-native-image

如果有使用 Docker,那第一種肯定是更好的方式,所有的依賴都打包到一個映象中了,避免了環境汙染。

最後總結一下就是,Spring Native 可以無需 JVM 執行,構建慢、啟動快、記憶體佔用少、執行最佳化少,另外還有很多 Java 特性受限,比如:反射、動態代理等都需要透過提前配置化,因為 Java 是一種動態連結的語言,原生應用都要提前編譯,這個像反射、動態代理這種特性就會受限。

另外,目前 Spring Native 還處於 Beta 測試版本,現階段肯定還會存在很多問題,未來可能也還會有變更,不過我會繼續關注的,後續我也會更新更多 Java 系列最新技術實戰文章,Java技術棧第一時間推送。請大家持續關注哦!

本節所有內容都是參考官網最新文件,可謂是做了第一個吃螃蟹的人,覺得我的文章對你用收穫的話,動動小手,給個在看、轉發,原創不易,棧長需要你的鼓勵。

版權申明:本文系 “Java技術棧” 原創,原創實屬不易,轉載、引用本文內容請註明出處,禁止抄襲、洗稿,請自重,尊重大家的勞動成果和智慧財產權,抄襲必究。