GraalVM技术通过为Java应用进行静态编译,帮助应用消除了冷启动和运行时内存占用高的问题。针对GraalVM应用,ARMS提供了静态插装方案,将应用运行时使用Java探针对字节码的改写逻辑调整到静态编译中,实现静态增强,提供开箱即用的可观测能力。
GraalVM作为一项前沿技术,如果之前未在生产场景使用过,建议您在测试环境充分验证后再考虑生产使用。此外,您在使用该方案接入ARMS过程中有任何问题,欢迎通过钉钉答疑群(群号:80805000690)与我们联系。
使用限制
应用本身已完成GraalVM静态编译适配,如果是Spring Boot应用,可以参考相关文档进行应用适配改造。
该方案需要调整GraalVM JDK实现,因此使用时需确保所使用的GraalVM JDK均为ARMS提供。
如果要使用GraalVM静态编译,其对环境有一些要求,具体要求请参见GraalVM官方文档。
目前针对GraalVM场景,ARMS仅支持核心的Traces和Metrics功能,暂不支持Arthas、持续剖析和内存快照等功能,另外由于GraalVM场景下,应用内存形态有变化,因此JVM监控中的元空间详情、非堆内存和直接缓冲区无数据是正常的。
接入操作
步骤一:安装依赖
GraalVM场景下,首先需要在环境中安装以下依赖:
根据自身应用所在地域,下载对应的GraalVM版本的ARMS探针。
当前仅支持以下地域,如有更多需求,请通过钉钉答疑群(群号:80805000690)与我们联系。
地域
公网地址
VPC地址
华东1(杭州)
wget "http://arms-apm-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
wget "http://arms-apm-cn-hangzhou.oss-cn-hangzhou-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
华东2(上海)
wget "http://arms-apm-cn-shanghai.oss-cn-shanghai.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
wget "http://arms-apm-cn-shanghai.oss-cn-shanghai-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
华北2(北京)
wget "http://arms-apm-cn-beijing.oss-cn-beijing.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
wget "http://arms-apm-cn-beijing.oss-cn-beijing-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
华北3(张家口)
wget "http://arms-apm-cn-zhangjiakou.oss-cn-zhangjiakou.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
wget "http://arms-apm-cn-zhangjiakou.oss-cn-zhangjiakou-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
华南1(深圳)
wget "http://arms-apm-cn-shenzhen.oss-cn-shenzhen.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
wget "http://arms-apm-cn-shenzhen.oss-cn-shenzhen-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
华南1金融云
无
wget "http://arms-apm-cn-shenzhen-finance-1.oss-cn-shenzhen-finance-1-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
解压文件后,进入ArmsAgentNative目录,执行以下命令在本地环境中安装探针。
sh install.sh
下载支持可观测能力的GraalVM JDK版本:graalvm-java17-23.0.4-ali-1.2b.tar.gz。
解压后在对应目录中执行以下命令:
graalvm-java17-23.0.4-ali-1.2b/bin/native-image --version
返回结果如下表示安装成功:
下载Maven(如果环境已有Maven,无需再次安装):apache-maven-3.8.4-bin.tar.gz。
解压后,将环境变量JAVA_HOME和MAVEN_HOME设置对应解压文件后的路径。
例如:
请将
/xxx/
换成实际路径。export MAVEN_HOME=/xxx/apache-maven-3.8.4 export PATH=$PATH:$MAVEN_HOME/bin export JAVA_HOME=/xxx/graalvm-java17-23.0.4-ali-1.2b export PATH=$PATH:$JAVA_HOME/bin
步骤二:引入依赖
为应用添加如下依赖:
请将代码中的路径/xxx/dynamic-configs
指向应用原始的动态配置文件地址。
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>arms-javaagent-native</artifactId>
<version>4.1.11</version>
<type>pom</type>
</dependency>
</dependencies>
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<fallback>false</fallback>
<buildArgs>
<arg>-H:ConfigurationFileDirectories=native-configs,/xxx/dynamic-configs</arg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
步骤三:添加access-filter-file.json文件
在应用目录下,添加一个名为access-filter-file.json的文件,其中内容为:
{ "rules": [
{"excludeClasses": "sun.launcher.LauncherHelper"}
]
}
添加access-filter-file.json文件用于确保GraalVM收集动态特性的探针不要收集sun.launcher.LauncherHelper
中的反射。sun.launcher.LauncherHelper
是JVM启动的类,其中用到的反射对于静态编译后的Native Image是不需要的,否则会在编译时产生错误。
步骤四:预执行
为了让ARMS探针对应用的动态增强代码被静态编译到最终的Native Image文件中,因此需要预先挂载ARMS探针对应用进行预执行,预执行需要确保应用核心代码分支都被执行,当前已提供一个相关脚本供您完成该过程。注意将应用的所有RESTful接口都按提示声明在脚本中,以便执行过程中的相关接口被正常调用触发业务执行。
参考以下脚本,按照注释完成自定义内容补充。
######## 请根据实际修改一下参数 # ARMS接入参数, 可调用DescribeTraceLicenseKey接口获取到对应的LicenseKey。AppName代表接入到ARMS的哪一个应用中,您可以根据需要自定义您的应用名,在分布式架构中,同一个应用内可以包含多个对等的实例。 export ARMS_LICENSEKEY= export ARMS_APPNAME= # 应用接口列表,例如PS=(interface1 interface2 interface3 interface4) export PS= # 应用端口,例如PORT="8080" export PORT= # Native image文件路径,静态编译后会在应用target目录下生成一个native image文件,例如NATIVE_IMAGE_FILE="target/graalvm-demo" export NATIVE_IMAGE_FILE= # 运行ARMS Native Agent的命令,例如JAVA_CMD="-javaagent:./arms-native/aliyun-java-agent-native.jar -jar target/graalvm-demo-1.0.0.jar" export JAVA_CMD= ########
挂载ARMS Java探针,进入预执行,搜集静态编译的配置项。
sh ArmsAgentNative/run.sh --collect --jvm --Carms
步骤五:静态编译
完成依赖添加后,按照如下步骤对应用进行静态编译:
开始静态编译。
mvn -Pnative package
运行静态编译后的项目。
sh ArmsAgentNative/run.sh --native --Carms
其他操作
构建Docker镜像
如果要针对完成GraalVM静态编译后的应用构建Docker镜像,由于其中已经包含了JDK等运行时必要的信息,直接将最终的Native Image文件作为一个可执行文件放到镜像中,按照正常构建Docker镜像流程进行镜像构建即可。
示例Dockerfile:
-Darms.licenseKey
和-Darms.appName
参数需调整为实际值。
FROM centos:latest
WORKDIR /app
COPY ./target/graalvm-demo /app
CMD ["/app/graalvm-demo","-Darms.licenseKey=xxx","-Darms.appName=xxx"]
压缩Native Image
在比较Native Image与原Java程序所占的空间大小时,我们会将JDK的大小也加在Java程序侧一起计算。因为Java程序必须要JDK支持,而Native Image是自举的,它已经包含了所有的依赖。
但是因为Native Image中为汇编代码,相比Java应用的字节码信息密度更低,表达相同的语义所需的代码量更大。因此随着应用规模的增大,Native Image的大小会逐渐超越Java程序 + JDK的大小,导致部署、传输的压力增大。此时我们可以借助压缩工具UPX来降低Native Image的大小。UPX可以将二进制可执行文件仍然压缩为二进制可执行文件,压缩后的文件无需解压即可直接运行,并且基本不会影响运行时的性能。
下图展示了压缩Native Image的效果:
将静态编译后的graalvm-demo
文件压缩为graalvm-demo-compressed
文件,压缩后的大小仅有之前的28.4%。
与Fat Jar包的比较:
原先包含了Spring Boot、RocketMQ等所有依赖的Fat Jar包大小为216MB,而压缩后的Native Image仅有47MB。
UPX使用方法:
下载并解压UPX工具。
假设UPX被解压到
$UPX_HOME
目录,执行以下命令完成压缩。$UPX_HOME/upx -9 -o path/to/output-file path/to/original-file
-9
:压缩程度为1~9,值越大压缩率越高,压缩所需时间越长。-o path/to/output-file
:压缩输出文件路径,path/to/output-file需要换成实际的文件路径。path/to/original-file
:被压缩的原始文件路径,path/to/original-file需要换成实际的文件路径。