1.1 Graalvm
1.1.1 简介
GraalVM 是一种高性能运行时,可显着提高应用程序性能和效率,非常适合微服务. 对于 Java 程序 GraalVM 负责将 Java 字节码编译成机器码,映像生成过程使用静态分析来查找可从主 Java 方法访问的任何代码,然后执行完全提前 (AOT) 编译。生成的本机二进制文件包含机器代码形式的整个程序,以便立即执行。
1.
1.1.2 环境准备
- Brew 安装
brew install --cask graalvm/tap/graalvm-ce-lts-java11
gu install native-image
1.2.1.2.
复制
- 下载安装
- 下载 GraalVM : https://www.graalvm.org/downloads/
- 配置环境变量:JAVA_HOME 和 PATH
- 运行 gu install native-image
1.1.3 运行 demo
// 创建 HelloWorld.java 文件
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
// 执行编译
javac HelloWorld.java
native-image HelloWorld
//运行
./helloworld
Hello, World!
1.2.3.4.5.6.7.8.9.10.11.12.13.1.2.3.4.5.6.7.8.9.10.11.12.13.
复制
1.2 Spring Native
1.2.1 简介
Spring Native 原生镜像可以在许多场景下降低工作负载,包括微服务,函数式服务,非常适合容器和 Kubernetes。优点:快速启动,提高峰值性能以及降低内存消耗缺点:构建过程慢, 预热后的运行时优化少常规 JVM 和此本机映像平台之间的主要区别:
- 在构建时会从主入口点对应用程序进行静态分析。
- 在构建时将未使用的零件删除。
- 反射,资源和动态代理需要配置。
- 类路径在构建时是固定的。
- 没有类延迟加载:可执行文件中附带的所有内容都将在启动时加载到内存中。
- 一些代码将在构建时运行。
- 一些 Java 切面类的特性未得到完全支持。组成模块Spring Native 由以下模块组成:
- spring-native:运行Spring Native所需的运行时依赖,还提供了Native hints API。
- spring-native-configuration:Spring AOT 插件使用的 Spring 类的配置提示,包括各种 Spring Boot 自动配置。
- spring-native-docs:参考指南,采用 asciidoc 格式。
- spring-native-tools:用于查看镜像构建配置和输出的工具。
- spring-aot:Maven 和 Gradle 插件公共的 AOT 转换基础架构。
- spring-aot-gradle-plugin:AOT 转换的 Gradle 插件。
- spring-aot-maven-plugin:AOT 转换的 Maven 插件。
- samples:包含各种演示功能用法的示例,也用于集成测试。
2. 快速上手
主要有两种的方式来构建 Spring Boot 原生应用:使用 Spring Boot Buildpacks Support 生成一个包含本地可执行文件的轻量级容器。使用 GraalVM native image Maven plugin support 来生成本地可执行文件。
2.1 通过 Buildpacks 上手
2.1.1 系统要求
需要安装 Docker,并至少分配 8G 内存, 以及更多的 CPU
2.1.2 初始化 DEMO 项目
通过 https://start.spring.io/ 初始化 demo 工程
2.1.3 构建原生应用
可以按以下命令构建本地应用程序:
- Maven $ mvn spring-boot:build-image
2.1.3.1 编译加速
资料:https://paketo.io/docs/howto/configuration/直接执行编译命令,因为要从 github 下载相关依赖,会非常慢,慢到你怀疑人生, 而且大多数时候会下载失败, 如图则可以通过以下方法进行加速
- 下载构建需要的文件到本地,或上传到 OSS
https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-21.3.0
graalvm-ce-java11-linux-amd64-21.3.0.tar.gz
graalvm-ce-java11-linux-amd64-21.3.0.tar.gz.sha256 (sha256校验码)
native-image-installable-svm-java11-linux-amd64-21.3.0.jar
native-image-installable-svm-java11-linux-amd64-21.3.0.jar.sha256 (sha256校验码)
1.2.3.4.5.1.2.3.4.5.
复制
- 在工程根目录 bindings/dependency-mapping 创建 binding 文件
- 创建 type 文件:echo ‘dependency-mapping’ >type
- 查看文件校验码
也可以通过 https://github.com/paketo-buildpacks/graalvm/blob/main/buildpack.toml 网站进行查看
- 在工程根目录 bindings/dependency-mapping 创建 mapping 文件
- 本地文件方式
echo 'file://{docker 内文件目录}/graalvm-ce-java11-linux-amd64-21.3.0.tar.gz'>3a1bc8eaf0518c128aaacb987ceb0b0e288776f48af630c11c01fd31122d93fa
echo 'file://{docker 内文件目录}/native-image-installable-svm-java11-linux-amd64-21.3.0.jar'>8958d4e0cad07340db0cf9e871776809e2f08fe0c93960f728fec75c4a96764f
1.2.1.2.
复制
- Oss 方式
echo 'https://max-volador.oss-cn-hangzhou.aliyuncs.com/graalvm/dependency/graalvm-ce-java11-linux-amd64-21.3.0.tar.gz'>3a1bc8eaf0518c128aaacb987ceb0b0e288776f48af630c11c01fd31122d93fa
echo 'https://max-volador.oss-cn-hangzhou.aliyuncs.com/graalvm/dependency/native-image-installable-svm-java11-linux-amd64-21.3.0.jar'>8958d4e0cad07340db0cf9e871776809e2f08fe0c93960f728fec75c4a96764f
1.2.1.2.
复制
- 最后一步, 修改 pom.xml 配置,配置完成后就可以快乐的执行编译命令
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>${repackage.classifier}</classifier>
<image>
<builder>paketobuildpacks/builder:tiny</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
<SERVICE_BINDING_ROOT>/bindings</SERVICE_BINDING_ROOT>
</env>
<!-- 文件挂载 -->
<bindings>
<binding>${basedir}/bindings/dependency-mapping:/bindings/dependency-mapping</binding>
<!--suppress UnresolvedMavenProperty -->
<binding><absolute-binding-path>: {docker 内文件目录} </binding>
</bindings>
</image>
</configuration>
</plugin>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.
2.1.4 运行原生应用
$ docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT
1.1.
复制
2.2 通过原生镜像的 Maven 插件上手
2.2.1 系统要求
- Docker 8G 以上内存
- Graalvm 环境
2.2.2 初始化 DEMO 项目
通过 https://start.spring.io/ 初始化 demo 工程
2.2.3 构建原生应用
$ mvn -Pnative-image package上面的命令会创建一个本地可执行文件,该可执行文件在 target 目录中。
2.2.4 运行原生应用程序
$ ./target/demo
3. 开发进阶
3.1 Spring AOT
Spring AOT构建插件旨在通过利用应用程序的上下文(类路径,配置)来生成和编译源代码,从而改善本机图像的兼容性和占用空间。
3.1.1 Maven
<build>
<plugins>
<!-- ... -->
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.10.4</version>
<configuration>
<removeSpelSupport>true</removeSpelSupport>
<removeUnusedConfig>true</removeUnusedConfig>
<removeYamlSupport>true</removeYamlSupport>
<removeXmlSupport>false</removeXmlSupport>
</configuration>
<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>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.
复制
3.1.2 配置 Spring AOT
- mode 切换插件真实为本地镜像编译器提供多少配置:
- native (默认)提供本地镜像以及代理的资源,初始化,代理和反射(使用自动配置提示)配置。
- native-init 如果仅希望提供初始化配置和替换,则应使用。
- native-agent 正在使用跟踪代理程序生成的配置作为基础,并且还为控制器等组件提供了其他提示。
- removeXmlSupporttrue 默认情况下设置为 true,优化空间占用,将其设置为 false 恢复 Spring XML 支持(XML converters, codecs and XML application context support)。
- removeSpelSupport默认情况下设置为 false,设置为 true 删除 Spring SpEL 支持以优化空间占用(应仅在不需要 SpEL 的应用中使用)。
- removeYamlSupport 默认情况下设置为 false,设置为则true 删除Spring Boot Yaml支持以优化空间占用。
- removeJmxSupport 默认情况下设置为 true,以优化空间占用,将其设置为false 恢复 Spring Boot JMX支持。
- verify 默认情况下设置为 true,执行一些自动验证以确保应用可以本地编译, 设置为 false 关闭验证。
- debugVerify 默认设置为false,设置为 true 时启用验证调试。
- removeUnusedConfig默认情况下设置为 true,设置为 false 禁用删除未使用的配置。
- failOnMissingSelectorHint 默认情况下设置为 true,如果没有为激活的选择器提供提示数据,则抛出错误,设置为 false 将插件从抛出错误切换为警告。
- [Experimental] buildTimePropertiesMatchIfMissing 默认设置为 true。将其设置为 false 意味着指定 matchIfMissing=true 的任何属性都将被覆盖且不报错。
- [Experimental] buildTimePropertiesChecks(实验)打开一些与属性相关的配置条件构建时间的评估。它必须至少包含 default-include-all或 default-exclude-all 的初始参数, 然后可以使用逗号分隔的前缀列表,以明确包含或排除(例如 default-include-all,!spring.dont.include.these.,!or.these 或 default-exclude-all,spring.include.this.one.though.,and.this.one)。前缀匹配最长的属性将会被应用(如果属性与多个前缀匹配)。
3.1.3. native-image 配置
Spring Native 通过 Spring AOT build plugin 在 META-INF/native-image 下自动生成配置文件 native-image.properties,reflect-config.json,proxy-config.json 或 resource-config.json:
3.2 Spring-native 扩展
3.2.1 通过 @NativeHint 实现
@NativeHint(options = "native-image 命令行参数",
types="反射类配置",
aotProxies="复杂代理类配置",
jdkProxies="JDK 动态代理类配置",
serializables="序列化类配置,
resources="资源文件配置",
initialization="应该在构建时或运行时显式初始化的类/包",
imports="共享类导入")
@NativeHint(options = {"-H:+ReportExceptionStackTraces"})
1.2.3.4.5.6.7.8.9.10.11.1.2.3.4.5.6.7.8.9.10.
复制
3.2.2 通过 配置文件实现
在 META-INF/native-image 下 ${groupId}.${artifactId} 新增配置文件
reflect-config.json: 反射配置
serialization-config.json: 序列化配置
proxy-config.json: 动态代理配置
resource-config.json: 外部文件配置
1.2.3.4.5.1.2.3.4.5.
复制
4. 常见问题
4.1 构建问题
4.1.1 在构建时意外初始化了 DataSize
Error: Classes that should be initialized at run time got initialized during image building:
org.springframework.util.unit.DataSize was unintentionally initialized at build time. To see why org.springframework.util.unit.DataSize got initialized use --trace-class-initialization
1.2.1.2.
复制
缺乏 spring-native 依赖项和Spring AOT plugin
4.1.2 构建本机映像时出现内存不足错误
Error: Image build request failed with exit status 137。native-image 会消耗大量 RAM,因此建议您使用至少 16G RAM 的计算机
4.1.3 Builder 生命周期 ‘creator’ 失败,状态码为 145
Error: Image build request failed with exit status 145。内存不足, 网络问题等
4.1.4 缺少资源包
Caused by: java.util.MissingResourceException:
Resource bundle not found javax.servlet.http.LocalStrings.
Register the resource bundle using the option
-H:IncludeResourceBundles=javax.servlet.http.LocalStrings.
1.2.3.4.1.2.3.4.
复制
4.2 运行问题
4.2.1 序列化&反序列化失败问题
反序列化失败
DemoResponse(data=null, code=null, message= null)
序列化失败
在 META-INF/native-image/${groupId}/${artifactId}/serialization-config.json 添加需要进行序列化的类信息
[
{
"name": "com.springnative.demo.response.DemoResponse"
}
]
1.2.3.4.5.6.7.8.9.1.2.3.4.5.6.7.8.9.
复制
4.2.2 反射问题
在 META-INF/native-image/${groupId}/${artifactId}/reflect-config.json 中添加需要用到反射的类信息
{
"name": "com.springnative.demo.response.DemoResponse",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allDeclaredClasses": true
}
1.2.3.4.5.6.7.8.9.1.2.3.4.5.6.7.8.9.
复制
4.2.3 apache httpclient 调用失败, 采用 java.net.http.HttpClient 替换
4.2.4 mybatis-plus 初始化问题 & mapper 失效问题 (待补充)
5. 参考资料
Spring Native documentationGraalvmgraalvm-ce-buildsSpring-native Githubpaketo