#云原生征文#云原生应用启动的天花板-SpringNative 实战 原创 精华

乐观的铅笔
发布于 2022-5-12 23:39
浏览
0收藏

1.1 Graalvm

1.1.1 简介

GraalVM 是一种高性能运行时,可显着提高应用程序性能和效率,非常适合微服务. 对于 Java 程序 GraalVM 负责将 Java 字节码编译成机器码,映像生成过程使用静态分析来查找可从主 Java 方法访问的任何代码,然后执行完全提前 (AOT) 编译。生成的本机二进制文件包含机器代码形式的整个程序,以便立即执行。

1.1.2 环境准备

  • Brew 安装
   brew install --cask graalvm/tap/graalvm-ce-lts-java11
   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 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 下载相关依赖,会非常慢,慢到你怀疑人生, 而且大多数时候会下载失败, 如图
#云原生征文#云原生应用启动的天花板-SpringNative 实战-鸿蒙开发者社区
则可以通过以下方法进行加速

  1. 下载构建需要的文件到本地,或上传到 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. 在工程根目录 bindings/dependency-mapping 创建 binding 文件
  2. 创建 type 文件:echo ‘dependency-mapping’ >type
  3. 查看文件校验码

也可以通过 https://github.com/paketo-buildpacks/graalvm/blob/main/buildpack.toml 网站进行查看

  1. 在工程根目录 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
  • 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. 最后一步, 修改 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>

2.1.4 运行原生应用

$ docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT

2.2 通过原生镜像的 Maven 插件上手

2.2.1 系统要求

  1. Docker 8G 以上内存
  2. 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>

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"})

3.2.2 通过 配置文件实现

在 META-INF/native-image 下 ${groupId}.${artifactId} 新增配置文件
reflect-config.json: 反射配置
serialization-config.json: 序列化配置
proxy-config.json: 动态代理配置
resource-config.json: 外部文件配置

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

缺乏 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.

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"
 }
]

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
}

4.2.3 apache httpclient 调用失败, 采用 java.net.http.HttpClient 替换

4.2.4 mybatis-plus 初始化问题 & mapper 失效问题 (待补充)

5. 参考资料

Spring Native documentation
Graalvm
graalvm-ce-builds
Spring-native Github
paketo
【本文正在参加云原生有奖征文活动】,活动链接:https://ost.51cto.com/posts/12598

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
1
收藏
回复
举报
回复
    相关推荐