SpringBoot項目GraalVM遷移
一些背景
一直想把項目遷移到使用GraalVM構建出的原生應用上,但是在前段時間的一次嘗試后,發現很難做到,其中一個最主要原因就在于我目前手頭上沒有X86架構的電腦。平時我使用的是一個M1處理器的MacBook,編譯出的Docker鏡像架構指令集也是Arm64的,無法在我的X86服務器啟動。原本想著就這樣算了,但是實際上我最近在和一個朋友做一個node的項目,這個項目上朋友計劃配合GitHub Action進行發布流水線的操作,我就去查了一個,沒想到還真查到Graal官方推出的一個Github Action插件,基于這里,我就去嘗試探索了下,發現還真可以編譯成功,最終也把項目在生產環境啟動起來了。但是在這個過程中踩了不少坑。
代碼變更
這里代碼變更主要分為3類,分別是Gradle配置,GitHub Action配置文件和手動補充一些需要被AOT進去的配置。值得注意的是,去年開始Oracle推出的Oracle GraalVM已經支持了G1垃圾收集器,不再是企業版獨享的功能,可以直接在編譯配置中指定,前提是使用了Oracle GraalVM,這個可以通過下面buildpacks進行配置,如果不配置可能使用的是其他JDK,基于版權原因它一定沒有G1回收器:
tasks.named<BootBuildImage>("bootBuildImage") {
buildpacks = listOf("docker.io/paketobuildpacks/oracle", "urn:cnb:builder:paketo-buildpacks/java-native-image")
environment = mapOf("BP_NATIVE_IMAGE_BUILD_ARGUMENTS" to
"""
-march=compatibility
--gc=G1
-R:MaxHeapSize=128m
""")
docker {
publish.set(true)
publishRegistry {
imageName.set("docker.io/mingchiuli/${rootProject.name}:${version}")
url.set("https://docker.io")
val un = System.getenv("DOCKER_USERNAME")
val pwd = System.getenv("DOCKER_PWD")
username.set(un)
password.set(pwd)
}
}
}
基于Spring boot的初始化配置,還需要對BootBuildImage這個任務進行一定配置,因為需要使用Spring官方提供的這個打包任務進行鏡像構建,故配置DockerHub的用戶名和密碼用來推送鏡像。在這里用戶名和密碼是通過環境變量注入的,需要和GitHub Action配置文件搭配。配置里的-march=compatibility主要作用是禁止CPU指令優化,不加的話,由于編譯機器的CPU和啟動機器CPU的差異,有可能啟動鏡像報錯CPU指令集不支持的問題。
name: GraalVM build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1
with:
java-version: '21' # See 'Options' section below for all supported versions
distribution: 'graalvm' # See 'Options' section below for all available distributions
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Example step
run: |
echo "GRAALVM_HOME: $GRAALVM_HOME"
echo "JAVA_HOME: $JAVA_HOME"
java --version
native-image --version
- name: Example step using Gradle plugin
run: ./gradlew bootBuildImage
env:
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PWD: ${{ secrets.DOCKERHUB_PWD }}
GitHub Action配置文件里對環境變量進行定義,就是項目里Gradle配置的DOCKER_USERNAME和DOCKER_PWD。這里的關鍵在于需要把gradle-wrapper.jar和gradle-wrapper.properties提交到GitHub倉庫里,否則會報錯找不到gradlew這個命令。
另外,像caffeine這樣的庫,可能它在程序運行的時候根據開發者緩存的配置動態加載了一些類進來,所以需要增加一些配置,如:
hints.reflection().registerType(
TypeReference.of("com.github.benmanes.caffeine.cache.SSMSA"),
MemberCategory.PUBLIC_FIELDS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
上線
上線我使用的是Docker Compose,關于生產環境的服務配置,只需要通過Docker容器啟動的時候環境變量注入就可以了。在Docker Compose文件的environment這個下面寫就OK。
之后的計劃
這次成功改造為native化可以說久旱逢甘霖,由于native化以后占用內存大大降低,使在有限的經濟條件下項目有了微服務化的可能,以前后端應用需要600M的內存占用,現在優化到200M上下:

啟動時間也有了大提升,之前啟動需要25秒左右,現在只需要3秒上下:

目前這個服務器內存節省出來很多內存,可以把它拆分出微服務了。之后這個項目的改造方向是應該是這個。
微服務化相關架構圖
由于目前并沒有太多空閑時間,所以我暫時應該不會開啟這個微服務化進程,但是流程圖可以先開始畫一下:
本文來自博客園,作者:imissinstagram,轉載請注明原文鏈接:http://www.rzrgm.cn/LostSecretGarden/p/18182056

浙公網安備 33010602011771號