前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring Native 初体验及对比

Spring Native 初体验及对比

原创
作者头像
schema
修改于 2022-11-06 06:54:00
修改于 2022-11-06 06:54:00
6.8K00
代码可运行
举报
文章被收录于专栏:HelloSchemaHelloSchema
运行总次数:0
代码可运行

| 导语:由于笔者工作项目上的平台产品特性设计原因,用户在平台产品使用过程中会产生数量不少的 Java Spring Boot 微服务,加上 Java 服务本身资源消耗大(尤其内存消耗),造成资源成本很高。因此考虑调研将 Java Spring Boot 服务包编译为本地可运行程序的方式,记录亦供参考。

参考:

1. 概述

Spring Native 是 Spring 团队和 GraalVM 团队合作的成果,可以将 Spring 应用通过 AOT(Ahead-of-Time,预先编译)技术编译为 Native Image(本地可执行程序,不是指容器镜像),从而获得快速启动、低内存消耗、即时峰值性能等特性,这样的特性在云原生时代显得尤为重要,但相应代价是编译构建时间更长。

Spring Native 的相关特性以及 GraalVM 的介绍网上已有不少,详见参考文档,本文主要记录体验过程的一些细节以及效果对比。代码示例见附件。

spring-boot-native-image.zip

2. Spring Native 体验过程记录

2.1 环境

体验测试都在 MacBook Pro 上,

芯片: M1 Pro,16c,

内存: 32g,

系统: macOS Monterey,Version 12.3.1,

GraalVM: 22.0.1 版本

JDK:openlogic-openjdk-11.jdk,JDK 11 版本

(补充:注意,如果是 Mac M1 芯片,GraalVM、JDK 使用 amd64 版本和 aarch64 版本性能会相差很多,aarch64 芯片架构版本原生支持 M1。本文一开始使用 amd64 的版本,发现出来的数据比之前在旧 MacBook Intel 芯片下的数据要差,后来改使用 aarch64 版本,各项数据要好很多。openlogic-openjdk-11.jdk 找不到 aarch64 版本的,改使用 zulu 构建版本,传送。)

2.2 GraalVM 安装

需要先安装 GraalVM 和配置 GRAALVM_HOME 环境变量(如 macOS 下 GRAALVM_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-java11-22.0.0.2/Contents/Home),否则编译 Spring Native 应用时会提示出错:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[ERROR] Failed to execute goal org.graalvm.buildtools:native-maven-plugin:0.9.10:build (build-native) on project rest-service-complete: Execution build-native of goal org.graalvm.buildtools:native-maven-plugin:0.9.10:build failed: GraalVM native-image is missing from your system.
[ERROR]  Make sure that GRAALVM_HOME environment variable is present.

按照 https://www.graalvm.org/22.0/docs/getting-started/macos/ 指引进行安装,下载传送

注意将解压包 mv 到对应位置后需要先执行 sudo xattr -r -d com.apple.quarantine /path/to/GRAALVM,注意路径后面不需要 Contents/Home 一截,否则如果先使用 GRAALVM 会提示程序损坏,即使后面补执行 xattr 也一样。

2.3 OpenJDK 11 安装

GraalVM 支持 Java 11、Java 15 或 Kotlin 1.5+, 不支持 Java 8。

使用了 OpenLogic Build 版本 OpenJDK 11,下载传送。 同样解压到对应位置,设置 JAVA_HOME(如 macOS 下 JAVA_HOME=/Library/Java/JavaVirtualMachines/openlogic-openjdk-11.jdk/Contents/Home),否则如果使用了低版本 Java,会提示错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
org/springframework/aot/maven/TestGenerateMojo has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0 

2.4 Spring Native 依赖及编译工具

体验测试代码见,只是简单的尝试,所以没有包含反射等特性,只测试简单的 Hello 接口和 OpenFeign 服务互调。

所以相比原本的 SpringBoot 应用不需要改动代码,只是在 pom.xml 文件中多加一个名为 native 的 profile。且当前 spring-native 0.11.3 版本只支持 Spring Boot 2.6.4 版本,所以需要注意 Spring Boot 和 Spring Cloud 版本的设置。如下:

代码语言:html
AI代码解释
复制
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <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>
    
    <profiles>
        <profile>
            <id>native</id>
            <dependencies>
                <!-- 运行Spring Native所需的运行时依赖,还提供了Native hints API-->
                <dependency>
                    <groupId>org.springframework.experimental</groupId>
                    <artifactId>spring-native</artifactId>
                    <version>0.11.4</version>
                </dependency>

                <!-- Required with Maven Surefire 2.x -->
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-launcher</artifactId>
                    <scope>test</scope>
                </dependency>
            </dependencies>
            <build>
                <plugins>
                    <!-- AOT 转换的 Maven 插件-->
                    <plugin>
                        <groupId>org.springframework.experimental</groupId>
                        <artifactId>spring-aot-maven-plugin</artifactId>
                        <version>0.11.4</version>
                        <executions>
                            <execution>
                                <id>generate</id>
                                <goals>
                                    <goal>generate</goal>
                                </goals>
                            </execution>
                            <execution>
                                <id>test-generate</id>
                                <goals>
                                    <goal>test-generate</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>

                    <!-- 提供编译和测试 native image 的支持,
                    see: https://graalvm.github.io/native-build-tools/latest/maven-plugin.html -->
                    <plugin>
                        <groupId>org.graalvm.buildtools</groupId>
                        <artifactId>native-maven-plugin</artifactId>
                        <version>0.9.11</version>
                        <extensions>true</extensions>
                        <executions>
                            <execution>
                                <id>build-native</id>
                                <goals>
                                    <goal>build</goal>
                                </goals>
                                <phase>package</phase>
                            </execution>
                            <execution>
                                <id>test-native</id>
                                <goals>
                                    <goal>test</goal>
                                </goals>
                                <phase>test</phase>
                            </execution>
                        </executions>
                        <configuration>
                            <!-- ... -->
                        </configuration>
                    </plugin>
                    <!-- Avoid a clash between Spring Boot repackaging and native-maven-plugin -->
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <classifier>exec</classifier>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

2.5 编译打包及对比

指定 -Pnative 使用 native profile 进行编译

代码语言:shell
AI代码解释
复制
GRAALVM_HOME='/Library/Java/JavaVirtualMachines/graalvm-ce-java11-22.0.0.2/Contents/Home' JAVA_HOME='/Library/Java/JavaVirtualMachines/openlogic-openjdk-11.jdk/Contents/Home/' mvn -Pnative -DskipTests package

编译过程长达 2min43s,主要时间消耗在 native-maven-plugin:0.9.10:build 即构建本地镜像过程中,且内存消耗也很大,最高达 8.15GB。 因此需要注意构建时分配多一些内存,防止出现 OOM。

native-maven-plugin:0.9.10:build 过程输出也很有意思,可以看到分为 7 个步骤,Performing analysis 分析阶段耗时最长,Compiling methods 编译方法次之。

代码语言:shell
AI代码解释
复制
GraalVM Native Image: Generating 'rest-service-complete'...
========================================================================================================================
[1/7] Initializing...                                                                                   (14.5s @ 0.44GB)
Warning: Could not register org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer: allDeclaredConstructors for reflection. Reason: java.lang.NoClassDefFoundError: org/springframework/jdbc/CannotGetJdbcConnectionException.
Warning: Could not register org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer: allDeclaredConstructors for reflection. Reason: java.lang.NoClassDefFoundError: javax/validation/ValidationException.
Warning: Could not register org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer: allDeclaredConstructors for reflection. Reason: java.lang.NoClassDefFoundError: liquibase/exception/ChangeLogParseException.
 Version info: 'GraalVM 22.0.0.2 Java 11 CE'
The bundle named: org.apache.tomcat.util.threads.res.LocalStrings, has not been found. If the bundle is part of a module, verify the bundle name is a fully qualified class name. Otherwise verify the bundle path is accessible in the classpath.
[2/7] Performing analysis...  [**********]                                                              (68.0s @ 2.82GB)
Warning: Could not register complete reflection metadata for org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer. Reason(s): java.lang.NoClassDefFoundError: org/springframework/jdbc/CannotGetJdbcConnectionException
Warning: Could not register complete reflection metadata for org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer. Reason(s): java.lang.NoClassDefFoundError: liquibase/exception/ChangeLogParseException
Warning: Could not register complete reflection metadata for org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer. Reason(s): java.lang.NoClassDefFoundError: javax/validation/ValidationException
Warning: Could not register complete reflection metadata for org.springframework.validation.beanvalidation.SpringValidatorAdapter$ViolationFieldError. Reason(s): java.lang.NoClassDefFoundError: javax/validation/Validator, java.lang.NoClassDefFoundError: javax/validation/ConstraintViolation
  14,554 (90.03%) of 16,165 classes reachable
  24,076 (75.73%) of 31,792 fields reachable
  71,665 (62.74%) of 114,217 methods reachable
     734 classes,   186 fields, and 3,373 methods registered for reflection
      68 classes,    89 fields, and    55 methods registered for JNI access
[3/7] Building universe...                                                                               (4.8s @ 3.84GB)
[4/7] Parsing methods...      [**]                                                                       (2.9s @ 5.21GB)
[5/7] Inlining methods...     [*****]                                                                    (5.4s @ 3.52GB)
[6/7] Compiling methods...    [******]                                                                  (33.0s @ 3.95GB)
[7/7] Creating image...                                                                                  (6.1s @ 3.11GB)
  29.39MB (42.70%) for code area:   47,456 compilation units
  34.04MB (49.46%) for image heap:  10,153 classes and 430,771 objects
   5.39MB ( 7.83%) for other data
  68.82MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 packages in code area:                               Top 10 object types in image heap:
   1.59MB sun.security.ssl                                    12.88MB byte[] for general heap data
1005.81KB java.util                                            3.56MB java.lang.Class
 941.17KB com.oracle.svm.core.reflect                          2.94MB java.lang.String
 673.61KB com.sun.crypto.provider                              2.50MB byte[] for java.lang.String
 608.80KB org.apache.tomcat.util.net                           1.52MB java.util.LinkedHashMap
 529.34KB org.apache.catalina.core                             1.10MB java.lang.reflect.Method
 495.82KB sun.security.x509                                  598.50KB java.util.HashMap$Node
 474.78KB org.apache.coyote.http2                            565.13KB java.lang.String[]
 451.74KB org.aspectj.weaver.patterns                        554.10KB com.oracle.svm.core.util.LazyFinalReference
 428.94KB java.util.concurrent                               463.83KB byte[] for method metadata
      ... 627 additional packages                                 ... 2962 additional object types
                                           (use GraalVM Dashboard to see all)
------------------------------------------------------------------------------------------------------------------------
                        9.8s (6.9% of total time) in 42 GCs | Peak RSS: 8.15GB | CPU load: 3.78
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /Users/schelling/IdeaProjects/spring-boot-native-image/target/rest-service-complete (executable)
 /Users/schelling/IdeaProjects/spring-boot-native-image/target/rest-service-complete.build_artifacts.txt
========================================================================================================================
Finished generating 'rest-service-complete' in 2m 19s.
[INFO]
[INFO] --- spring-boot-maven-plugin:2.6.4:repackage (repackage) @ rest-service-complete ---
[INFO] Attaching repackaged archive /Users/schelling/IdeaProjects/spring-boot-native-image/target/rest-service-complete-0.0.1-SNAPSHOT-exec.jar with classifier exec
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:43 min
[INFO] Finished at: 2022-05-04T18:01:13+08:00
[INFO] Final Memory: 41M/174M
[INFO] ------------------------------------------------------------------------

 与之相比,如果不使用 native 方式编译,耗时在 4s 左右。

代码语言:shell
AI代码解释
复制
~ JAVA_HOME='/Library/Java/JavaVirtualMachines/openlogic-openjdk-11.jdk/Contents/Home/' mvn -DskipTests package

...
[INFO] Total time: 4.140 s
...

 编译完同时生成本地可执行程序和 jar 包,可以看到本地可执行镜像也不小,有 64MB, jar 包反而要小些,这也正常,毕竟 jar 包还需要 jvm。

2.6 运行对比

(1) Native Image 运行

直接运行可执行文件,启动提示为 0.663s ,启动后内存占用 71.4MB。

(很奇怪的是,之前在 intel 芯片的 MacBook Pro 上测试的数值没有这么高,猜测跟 M1 芯片下 Rosetta 转化有关系,待确认。之前在 intel 芯片上 ,启动完差不多 21MB,调用了几次接口后是 23.7MB。启动非常快,几毫秒。)

(2) Java 包运行

与之相比,直接 java -jar 运行 jar 包方式的话,启动提示为 4.295s , 启动后内存占用 513.3MB,对比差别挺大。

(之前在 intel 芯片上 使用 jar 包启动的,启动完是 191.3MB,而且启动接近 1s。 同样待确认)

(3) 测试服务间互调-使用 OpenFeign

尝试写了一个 Provider 和 一个 Comsumer,直接 @FeignClient 中指定 url,可以调通。

且发现 java -jar -Dspring.profiles.active=8090 rest-service-complete-0.0.1-SNAPSHOT.jar 这样的启动中传入参数,在 native image 方式下,也可以传入参数,即 ./rest-service-complete -Dspring.profiles.active=8090 可运行成功。

(4) Mac M1 芯片下使用 aarch64 版本 GraalVM 和 JDK

(补充) 上面猜测可能由于 M1 芯片下使用非 M1 芯片版本的 GraalVM 和 JDK,影响构建和运行性能,因为查找下载了针对 M1 芯片的 aarch64 版本,各项目数据相比非 aarch64 版本下的要好很多,补充记录于“对比总览”中。

2.7 对比总览

原 Java (非 arrch64 版本)

Spring Native (非 arrch64 版本)

原 Java (aarch64 版本)

Spring Native (aarch64 版本)

编译时间

4.140s

163s

1.403s

60s

包/可执行文件大小

27MB

64MB

27MB

64MB

启动时间

4.295s

0.663s

1.157s

0.051s

启动后内存占用

513.3MB

71.4MB

227.1MB

36.5MB

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
【wiki知识库】08.添加用户登录功能--前端Vue部分修改
这个事情不难,说的简单一些呢就是前端会从你的本地浏览器的存储空间中找出有没有一个可以证明你登陆过的token,如果有这个token就会展示对应的功能选项,否则的话就不会展示。这个token就是在我们登录之后返回给前端的,并且由前端来将其保存到本地浏览器的一个存储空间当中。
哈__
2024/08/19
1090
【wiki知识库】08.添加用户登录功能--前端Vue部分修改
测试需求平台16-产品管理搜索功能实现
在系列前几篇实战分享中,我们从0-1实现了产品管理的中的列表显示和增删改的最为基础的交互操作,本篇将继续学习和实现其搜索和分页。
MegaQi
2023/10/21
1640
测试需求平台16-产品管理搜索功能实现
【wiki知识库】04.SpringBoot后端实现电子书的增删改查以及前端界面的展示
今天主要是把这个网页的界面初步优化一下,修改一下导航栏以及增加电子书管理模块。包含电子书的查询功能、新增功能、编辑功能和删除功能(不包括文档管理)。
哈__
2024/06/04
1650
【wiki知识库】04.SpringBoot后端实现电子书的增删改查以及前端界面的展示
测试需求平台11-产品管理交互Acro必要组件掌握
✍ 此系列为整理分享已完结入门搭建《TPM提测平台》系列的迭代版,拥抱Vue3.0将前端框架替换成字节最新开源的arco.design,其中约60%重构和20%新增内容,定位为从 0-1手把手实现简单的测试平台开发教程,内容将囊括基础、扩展和实战,由浅入深带你实现测试开发岗位中平台工具技术能力入门和提升。
MegaQi
2023/10/21
3670
测试需求平台11-产品管理交互Acro必要组件掌握
【wiki知识库】07.用户管理前端模块的添加-前端部分
这一部分的内容不再多说了,添加一个菜单很简单,只需要在文件中多加一个item,然后item中加上一个路由跳转的标签。最后在配置一下路由就可以了。
哈__
2024/08/06
1060
【wiki知识库】07.用户管理前端模块的添加-前端部分
【wiki知识库】05.分类管理实现--前端Vue模块
除了分类管理,我们的首页也变动了一下。首页的导航栏加载的是我们已经有的分类,同时还加上了一个欢迎页面。
哈__
2024/06/06
1240
【wiki知识库】05.分类管理实现--前端Vue模块
如果你觉得写代码很难,那么请认真读完这篇文章,会让你找到coding的正确姿势!
秒变正经,进入正题,其实还是围绕Ant Design Vue中组件的使用展开,相信我,这并不难。
软件测试君
2022/03/31
7620
如果你觉得写代码很难,那么请认真读完这篇文章,会让你找到coding的正确姿势!
vue 页面逻辑复用
在开发表单页面时, 经常碰到一种情况。 同一表单存在 新建 编辑 查看 几种状态。页面的大体组成基本相同,但又需要做部分针对当前状态的处理,处理这类问题,一般存在两种方式
copy_left
2019/08/29
1.5K0
使用antd遇到的问题
form标签里面嵌套了三个form标签,仔细检查了自己复制的代码没有问题,那就是引入的时候哪个环节出错了。于是检查引入组件的代码。
前端小tips
2021/12/06
7860
使用antd遇到的问题
【wiki知识库】06.文档管理页面的添加--前端Vue部分
在此之前我要要说一件事情,我在做这个模块的时候出现了问题,一个是我们之后要使用的文本编辑器wangeditor无法正常展示,还有一个是弹窗无法关闭的问题。这里我把解决方法告诉大家。
哈__
2024/06/10
2020
【wiki知识库】06.文档管理页面的添加--前端Vue部分
测试需求平台17-产品管理分页功能和样式优化
数据较多时候使用分页控制信息数量,也可以进行页面的转跳,常搭配 列表List 或 表格Table 使用。
MegaQi
2023/10/21
2250
测试需求平台17-产品管理分页功能和样式优化
测试需求平台13-Table组件应用产品列表优化
气泡确认框是一种轻量的反馈方式,承载的内容也相对较少,主要用于二次确认操作。对比较为常规的对话框二次确认,气泡确认框从形式上更轻量,干扰更小,控件的打开关闭方式也更为便捷
MegaQi
2023/10/21
2630
测试需求平台13-Table组件应用产品列表优化
基于Ant Design Vue封装一个表单控件
https://github.com/naturefwvue/nf-vue3-ant
用户1174620
2020/09/19
3.2K0
讲一讲Vue+Ant Design表单验证
与Vue搭配的后台管理UI框架,最火的莫过于饿了吗的element-ui和阿里的Ant Design,这两个框架都在实际项目上使用过,也都是各有各的优点
青年码农
2021/01/18
3.6K0
讲一讲Vue+Ant Design表单验证
疫情期间,写的两个场景
嗯~这种实现的方式还是和舒服的,不用自己布局,不用自己再次思考逻辑;如果你想自己捣鼓一个,那你是真的闲,还不如花点时间捣鼓其他非编程的东西。
Jimmy_is_jimmy
2020/04/01
1.1K0
疫情期间,写的两个场景
Vue 折腾记 - (17) 基于Ant Design Vue 封装一个配置式的表单组件
所以选择了slot来实现,如何保证传入的form-item的布局一致,则是拿slot-scope
CRPER
2019/04/23
4.2K1
Vue 折腾记 - (17) 基于Ant Design Vue 封装一个配置式的表单组件
记录下:订单模块初步完成
哇,好久没有记录自己写代码的总结了,今天记录下,点赞,订阅,转发,感谢各位老铁的支持与厚爱。
公众号---人生代码
2021/07/30
9160
Vue 折腾记 - (16) 基于Ant Design Vue 封装一个配置式的表单搜索组件
React 折腾记 - (6) 基于React 16.x+ Antd 3.封装的一个声明式的查询组件(实用强大)
CRPER
2019/04/18
8.5K0
Vue 折腾记 - (16) 基于Ant Design Vue 封装一个配置式的表单搜索组件
48·灵魂前端工程师养成-Vue表单和v-model
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
DriverZeng
2022/11/08
5930
48·灵魂前端工程师养成-Vue表单和v-model
Egg.js试水 - 文章增删改查【前后端分离】
上一篇文章讲的是后端渲染的项目 - Egg.js 试水 - 天气预报。但是没有引入数据库。这次的试水项目是文章的增删改查,将数据库引进,并且实现前后端分离。
Jimmy_is_jimmy
2020/10/15
3.4K0
推荐阅读
相关推荐
【wiki知识库】08.添加用户登录功能--前端Vue部分修改
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档