graalvm-native 是将java 的.jar 代码进行AOT编译,编译成二进制,将代码和运行环境jre一起打包二进制,做到直接运行
graalvem-native 现状
graalvm 一个让java aot的技术。主要的使用场景在于启动快速,尤其是在云服务场景下,能够快速启动就能实现秒级的弹性扩容。至于占用内存小其实用处不大,在当前的容器化服务中,大家都是打包一个jvm和一个应用镜像,使用了native技术后,就可以直接java二进制运行,实现了更快的扩容及启动
问题点
对于反射和动态代理,这种在运行时才知道类的技术,无法做到。因此要在编译时指定反射的哪些类和代理的类的范围才行。
使用reflect-config.json 配置文件来实现这个上面这个操作
主要使用方式
下一个graalvm, 可以当成jvm用,里面带有一个gu,还有native-image,指定一下path路径到graalvm里面,
- 可以使用native-image -jar xxx.jar xxx 来编译原生包到二进制
native-image --initialize-at-build-time=org.slf4j.LoggerFactory -jar db-proxy-1.0.0-jar-with-dependencies.jar db-proxy1
注意 这个jar如果要用maven 打包的话得打包出来reflect-config.json这个就就是反射配置 -
还可以基于spring 和maven来添加native 插件形式来实现
https://www.graalvm.org/22.2/reference-manual/native-image/guides/use-native-image-maven-plugin/
spring boot native
spring boot3 支持native,大部分组件可以做到升级到spring boot 3 就可以实现native,
但是mybatis 这个组件在当前时间要进行比较大的改动,要改成<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
然后还有很多的初始化,我是参考了plus 给的spring样例工程才成功,具体下一篇文章
<version>3.5.4.1</version>
不过最大的问题是spring boot 2 到spring boot3 中es 的api升级变更最大
spring boot 提供了一个RuntimeHintsRegistrar 来替换reflect-config.json 文件,来实现对没有编译到的class文件进行编译并指定其入参,替换里面的不确定的类型
这个spring 的替换我会在下面一篇文章专门讲一下,主要是有很多小细节的问题
mvn -Pnative native:compile
执行这个命令即可进行编译
其实就是 先使用packet 打包
然后执行 native-image -jar
maven native
不使用spring 的应用如果要进行native,其实比spring 的要复杂一些
因为spring 有hits, 而原生程序只能使用reflect-config.json 文件
先修改pom ,添加assmbly 打包,因为你要把所有的依赖打到一个jar里面,然后再使用native-image 命令将这个可运行的包打成二进制
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.12</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>
<fallback>false</fallback>
<buildArgs>
<!--<arg>-H:DashboardDump=fortune -H:+DashboardAll</arg>
--> </buildArgs>
<!--<agent>
<enabled>true</enabled>
<options>
<option>experimental-class-loader-support</option>
</options>
</agent>-->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<!--<addClasspath>true</addClasspath>-->
<mainClass>com.wps.dbproxy.DatabaseProxyApplication</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
然后 mvn -Pnative package -DskipTests
执行即可,
遇到问题咋办-如何生成reflect-config.json 文件
这时候你可能会遇到一些问题,就是说一些类不可预估的被初始化了
Error: Classes that should be initialized at run time got initialized during image building:
org.slf4j.LoggerFactory was unintentionally initialized at build time
一般这种情况就去改reflect-config.json 这个就是我们开头提到的问题,反射没有指定导致的问题。如果想要快速解决可以使用
java -jar -agentlib:native-image-agent=config-output-dir=./native-image ./target/xxx-your-jar-with-dependencies.jar
然后执行一些你的业务,这个是一个动态记录,如果你的业务没走到这个反射代码里面去,就不会记录,这个有点坑。
使用这个收集一下反射情况,然后ctrl+C 就能在当前的目录的./native-image 里面获取到reflect-config.json文件
然后把这个文件,方法到 resource/resources/META-INF/native-image/xxxx/xxx/里面,比如我放到了resources/META-INF/native-image/ch.qos.logback/logback-classic/1.4.11
然后再次执行编译命令,如果还出现那个问题就自己手动在reflect-config.json 中添加一下那些反射,目前处于初级阶段
agent reflect-config.json 文件介绍
{
“name”:”com.fasterxml.jackson.databind.Module”,
“allDeclaredFields”:true,
“queryAllDeclaredMethods”:true,
“methods”:[{“name”:”getDependencies”,”parameterTypes”:[] }]
}
name 是类名
methods 是表示 这个类的函数,paramenterTypes 表示这个函数的入参
因为有些是动态反射或者动态调用的,就得给graalvm 指定函数出入参 指定这个类的函数
ClassNotFoundException
reflect-config.json 的文件标准
https://www.graalvm.org/22.0/reference-manual/native-image/Agent/
发表回复