王炸!!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 語言等,可提高多種語言的運行速度和吞吐量。

GraalVM 有以下幾個特性。

  • 更加高效快速的運行代碼
  • 能與大多數編程語言直接交互
  • 使用 Graal SDK 嵌入多語言
  • 創建預編譯的原生鏡像
  • 提供一系列工具來監視、調試和配置所有代碼

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

重點來看原生鏡像功能:

$ javac HelloWorld.java
$ time java HelloWorld
user 0.070s
$ native-image HelloWorld
$ time ./helloworld
user 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 Boot:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.5</version>
    <relativePath/>
</parent>

Spring Native:

<dependencies>
    <dependency>
        <groupId>org.springframework.experimental</groupId>
        <artifactId>spring-native</artifactId>
        <version>${spring-native.version}</version>
    </dependency>
</dependencies>

注意依賴版本:

Spring Native 最新版本為:0.9.2,只支持 Spring Boot 2.4.5

3、添加 Spring AOT 插件

添加 Spring AOT 插件:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.experimental</groupId>
            <artifactId>spring-aot-maven-plugin</artifactId>
            <version>0.9.2</version>
            <executions>
                <execution>
                    <id>test-generate</id>
                    <goals>
                        <goal>test-generate</goal>
                    </goals>
                </execution>
                <execution>
                    <id>generate</id>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Spring AOT 插件執行所需的提前轉換,以提升原生鏡像的兼容性。

4、開啟原生鏡像支持

在 Spring Boot Maven 插件中增加以下配置:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <image>
            <builder>paketobuildpacks/builder:tiny</builder>
            <env>
                <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
            </env>
        </image>
    </configuration>
</plugin>

5、添加 Maven 倉庫支持

Spring Native 依賴和插件需要在 Spring 倉庫中下載,需要添加以下配置。

<repositories>
    <repository>
        <id>spring-release</id>
        <name>Spring release</name>
        <url>https://repo.spring.io/release</url>
    </repository>
</repositories>

<pluginRepositories>
    <pluginRepository>
        <id>spring-release</id>
        <name>Spring release</name>
        <url>https://repo.spring.io/release</url>
    </pluginRepository>
</pluginRepositories>

如果不能正常下載 Native 依賴和插件,需要檢查 Maven 的 settings.xml 文件:

<mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>*,!spring-release</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

把 mirrorOf 值由 * 修改為:*,!spring-release

6、添加測試接口

添加一個測試接口,原生應用啟動后,方便測試下可行性。

/**
 * 微信公眾號:Java技術棧
 */
@SpringBootApplication
@RestController
public 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 Boot 應用程序,這個構建卻過程花了我 4 分鍾。。

8、運行原生應用

使用平常運行 Docker 鏡像的方式就能運行原生應用:

docker run --rm -p 8080:8080

當然也可以在項目中編寫 docker-compose.yml 文件的方式,這里不再演示,感興趣的可以關注公眾號:Java技術棧,在歷史文章中搜索閱讀 Docker 系列文章。

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

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

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

再來訪問我們之前寫的接口:

http://localhost:8080/native/hi

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

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

然后我們用傳統 JVM 環境來運行下:

java -jar spring-boot-native-1.0.jar

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

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

我們再來比對下包的大小

查看剛生成的 Docker 鏡像:

docker image ls

查看基於 JVM 的可執行 jar 包:

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技術棧" 原創,原創實屬不易,轉載、引用本文內容請注明出處,禁止抄襲、洗稿,請自重,尊重大家的勞動成果和知識產權,抄襲必究。

近期熱文推薦:

1.600+ 道 Java面試題及答案整理(2021最新版)

2.終於靠開源項目弄到 IntelliJ IDEA 激活碼了,真香!

3.阿里 Mock 工具正式開源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式發布,全新顛覆性版本!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM