將GraalVM應(yīng)用接入ARMS
GraalVM技術(shù)通過(guò)為Java應(yīng)用進(jìn)行靜態(tài)編譯,幫助應(yīng)用消除了冷啟動(dòng)和運(yùn)行時(shí)內(nèi)存占用高的問(wèn)題。針對(duì)GraalVM應(yīng)用,ARMS提供了靜態(tài)插裝方案,將應(yīng)用運(yùn)行時(shí)使用Java探針對(duì)字節(jié)碼的改寫邏輯調(diào)整到靜態(tài)編譯中,實(shí)現(xiàn)靜態(tài)增強(qiáng),提供開(kāi)箱即用的可觀測(cè)能力。
GraalVM作為一項(xiàng)前沿技術(shù),如果之前未在生產(chǎn)場(chǎng)景使用過(guò),建議您在測(cè)試環(huán)境充分驗(yàn)證后再考慮生產(chǎn)使用。此外,您在使用該方案接入ARMS過(guò)程中有任何問(wèn)題,歡迎通過(guò)釘釘答疑群(群號(hào):80805000690)與我們聯(lián)系。
使用限制
應(yīng)用本身已完成GraalVM靜態(tài)編譯適配,如果是Spring Boot應(yīng)用,可以參考相關(guān)文檔進(jìn)行應(yīng)用適配改造。
該方案需要調(diào)整GraalVM JDK實(shí)現(xiàn),因此使用時(shí)需確保所使用的GraalVM JDK均為ARMS提供。
如果要使用GraalVM靜態(tài)編譯,其對(duì)環(huán)境有一些要求,具體要求請(qǐng)參見(jiàn)GraalVM官方文檔。
目前針對(duì)GraalVM場(chǎng)景,ARMS僅支持核心的Traces和Metrics功能,暫不支持Arthas、持續(xù)剖析和內(nèi)存快照等功能,另外由于GraalVM場(chǎng)景下,應(yīng)用內(nèi)存形態(tài)有變化,因此JVM監(jiān)控中的元空間詳情、非堆內(nèi)存和直接緩沖區(qū)無(wú)數(shù)據(jù)是正常的。
接入操作
步驟一:安裝依賴
GraalVM場(chǎng)景下,首先需要在環(huán)境中安裝以下依賴:
根據(jù)自身應(yīng)用所在地域,下載對(duì)應(yīng)的GraalVM版本的ARMS探針。
當(dāng)前僅支持以下地域,如有更多需求,請(qǐng)通過(guò)釘釘答疑群(群號(hào):80805000690)與我們聯(lián)系。
地域
公網(wǎng)地址
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金融云
無(wú)
wget "http://arms-apm-cn-shenzhen-finance-1.oss-cn-shenzhen-finance-1-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip
解壓文件后,進(jìn)入ArmsAgentNative目錄,執(zhí)行以下命令在本地環(huán)境中安裝探針。
sh install.sh
下載支持可觀測(cè)能力的GraalVM JDK版本:graalvm-java17-23.0.4-ali-1.2b.tar.gz。
解壓后在對(duì)應(yīng)目錄中執(zhí)行以下命令:
graalvm-java17-23.0.4-ali-1.2b/bin/native-image --version
返回結(jié)果如下表示安裝成功:
下載Maven(如果環(huán)境已有Maven,無(wú)需再次安裝):apache-maven-3.8.4-bin.tar.gz。
解壓后,將環(huán)境變量JAVA_HOME和MAVEN_HOME設(shè)置對(duì)應(yīng)解壓文件后的路徑。
例如:
請(qǐng)將
/xxx/
換成實(shí)際路徑。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
步驟二:引入依賴
為應(yīng)用添加如下依賴:
請(qǐng)將代碼中的路徑/xxx/dynamic-configs
指向應(yīng)用原始的動(dòng)態(tài)配置文件地址。
<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文件
在應(yīng)用目錄下,添加一個(gè)名為access-filter-file.json的文件,其中內(nèi)容為:
{ "rules": [
{"excludeClasses": "sun.launcher.LauncherHelper"}
]
}
添加access-filter-file.json文件用于確保GraalVM收集動(dòng)態(tài)特性的探針不要收集sun.launcher.LauncherHelper
中的反射。sun.launcher.LauncherHelper
是JVM啟動(dòng)的類,其中用到的反射對(duì)于靜態(tài)編譯后的Native Image是不需要的,否則會(huì)在編譯時(shí)產(chǎn)生錯(cuò)誤。
步驟四:預(yù)執(zhí)行
為了讓ARMS探針對(duì)應(yīng)用的動(dòng)態(tài)增強(qiáng)代碼被靜態(tài)編譯到最終的Native Image文件中,因此需要預(yù)先掛載ARMS探針對(duì)應(yīng)用進(jìn)行預(yù)執(zhí)行,預(yù)執(zhí)行需要確保應(yīng)用核心代碼分支都被執(zhí)行,當(dāng)前已提供一個(gè)相關(guān)腳本供您完成該過(guò)程。注意將應(yīng)用的所有RESTful接口都按提示聲明在腳本中,以便執(zhí)行過(guò)程中的相關(guān)接口被正常調(diào)用觸發(fā)業(yè)務(wù)執(zhí)行。
參考以下腳本,按照注釋完成自定義內(nèi)容補(bǔ)充。
######## 請(qǐng)根據(jù)實(shí)際修改一下參數(shù) # ARMS接入?yún)?shù), 可調(diào)用DescribeTraceLicenseKey接口獲取到對(duì)應(yīng)的LicenseKey。AppName代表接入到ARMS的哪一個(gè)應(yīng)用中,您可以根據(jù)需要自定義您的應(yīng)用名,在分布式架構(gòu)中,同一個(gè)應(yīng)用內(nèi)可以包含多個(gè)對(duì)等的實(shí)例。 export ARMS_LICENSEKEY= export ARMS_APPNAME= # 應(yīng)用接口列表,例如PS=(interface1 interface2 interface3 interface4) export PS= # 應(yīng)用端口,例如PORT="8080" export PORT= # Native image文件路徑,靜態(tài)編譯后會(huì)在應(yīng)用target目錄下生成一個(gè)native image文件,例如NATIVE_IMAGE_FILE="target/graalvm-demo" export NATIVE_IMAGE_FILE= # 運(yùn)行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探針,進(jìn)入預(yù)執(zhí)行,搜集靜態(tài)編譯的配置項(xiàng)。
sh ArmsAgentNative/run.sh --collect --jvm --Carms
步驟五:靜態(tài)編譯
完成依賴添加后,按照如下步驟對(duì)應(yīng)用進(jìn)行靜態(tài)編譯:
開(kāi)始靜態(tài)編譯。
mvn -Pnative package
運(yùn)行靜態(tài)編譯后的項(xiàng)目。
sh ArmsAgentNative/run.sh --native --Carms
其他操作
構(gòu)建Docker鏡像
如果要針對(duì)完成GraalVM靜態(tài)編譯后的應(yīng)用構(gòu)建Docker鏡像,由于其中已經(jīng)包含了JDK等運(yùn)行時(shí)必要的信息,直接將最終的Native Image文件作為一個(gè)可執(zhí)行文件放到鏡像中,按照正常構(gòu)建Docker鏡像流程進(jìn)行鏡像構(gòu)建即可。
示例Dockerfile:
-Darms.licenseKey
和-Darms.appName
參數(shù)需調(diào)整為實(shí)際值。
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程序所占的空間大小時(shí),我們會(huì)將JDK的大小也加在Java程序側(cè)一起計(jì)算。因?yàn)?span id="z68uejxpaoma" class="help-letter-space">Java程序必須要JDK支持,而Native Image是自舉的,它已經(jīng)包含了所有的依賴。
但是因?yàn)?span id="z68uejxpaoma" class="help-letter-space">Native Image中為匯編代碼,相比Java應(yīng)用的字節(jié)碼信息密度更低,表達(dá)相同的語(yǔ)義所需的代碼量更大。因此隨著應(yīng)用規(guī)模的增大,Native Image的大小會(huì)逐漸超越Java程序 + JDK的大小,導(dǎo)致部署、傳輸?shù)膲毫υ龃蟆4藭r(shí)我們可以借助壓縮工具UPX來(lái)降低Native Image的大小。UPX可以將二進(jìn)制可執(zhí)行文件仍然壓縮為二進(jìn)制可執(zhí)行文件,壓縮后的文件無(wú)需解壓即可直接運(yùn)行,并且基本不會(huì)影響運(yùn)行時(shí)的性能。
下圖展示了壓縮Native Image的效果:
將靜態(tài)編譯后的graalvm-demo
文件壓縮為graalvm-demo-compressed
文件,壓縮后的大小僅有之前的28.4%。
與Fat Jar包的比較:
原先包含了Spring Boot、RocketMQ等所有依賴的Fat Jar包大小為216MB,而壓縮后的Native Image僅有47MB。
UPX使用方法:
下載并解壓UPX工具。
假設(shè)UPX被解壓到
$UPX_HOME
目錄,執(zhí)行以下命令完成壓縮。$UPX_HOME/upx -9 -o path/to/output-file path/to/original-file
-9
:壓縮程度為1~9,值越大壓縮率越高,壓縮所需時(shí)間越長(zhǎng)。-o path/to/output-file
:壓縮輸出文件路徑,path/to/output-file需要換成實(shí)際的文件路徑。path/to/original-file
:被壓縮的原始文件路徑,path/to/original-file需要換成實(shí)際的文件路徑。