在GraalVM中部署執行Spring Boot應用 - Indrek Ots

banq發表於2020-01-13

GraalVM是一種高效能的多語言虛擬機器,用於執行以JavaScript等基於LLVM的各種語言編寫的應用程式。對於Java應用也可作為通常JVM的替代,它更具有效能優勢。GraalVM帶來的一個有趣功能是它能夠在建立JVM應用程式的提前編譯(create ahead-of-time:AOT)本機映象,從而保證了更快的啟動時間和更低的記憶體佔用。在本文中,我們將重點介紹如何建立Spring應用的本機二進位制檔案。

GraalVM Native Image 101

Java應用程式使用編譯為位元組碼javac。在應用程式執行時,JVM將類檔案載入到記憶體中,並分析程式的效能以獲取熱點。因此名稱為“ HotSpot JVM ”。在剛剛在時間(JIT)編譯器編譯這些重複執行為本機程式碼應用程式的部分。 但是,JIT編譯需要處理器時間和記憶體,這會影響應用程式的啟動時間。

GraalVM原生本機映象能提前將 JVM應用程式編譯為當前機器的機器程式碼。它靜態分析應用程式的位元組碼,找到所有可以訪問的類和方法,並將它們編譯為本地可執行檔案。輸出是特定於平臺的可執行二進位制檔案。

例如,讓我們從以下“ Hello World”程式構建原生映象。

class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello World");
  }
}

首先,我們需要使用 javac:

$ javac HelloWorld.java

然後用native-image將這個類檔案構建成可執行二進位制檔案。

$ native-image HelloWorld

要啟動應用程式,只需:

$ ./helloworld
 Hello World

本機映象Java限制

GraalVM本機映象靜態分析是需要假設在封閉世界的前提下。它需要在映象生成期間提前知道所有可能訪問的位元組碼。因此,並非所有Java功能都受支援:例如,不支援動態類的載入/解除安裝。反射需要配置。CGLIB代理不適用,但是卻支援JDK代理,還需要配置。此外,您還需要告訴本機映象有關所有資源訪問的資訊。

配置以JSON文件的形式提供。例如,要配置反射,您可以建立以下檔案,並使用-H:ReflectionConfigurationFiles命令列標誌來指定命令的檔案位置native-image。

[
  { "name":"java.lang.Object" },
  { "name":"org.apache.naming.factory.ResourceFactory", "methods" : [{"name": "<init>","parameterTypes":[]}] },
  ...
]

配置動態代理、JNI和資源訪問時都需要建立類似的檔案來。但是,手工完成所有這些工作需要很多工作,尤其是在我們處理大型應用程式時。幸運的是,有一個Java代理可以生成配置。它觀察在JVM中執行的應用程式的行為,並生成生成本機映像所需的配置檔案。

要獲得完整的配置檔案集,您需要在應用程式中使用所有程式碼路徑。具有100%覆蓋率的測試套件可以解決問題,但實際上,測試套件永遠不會測試所有路徑。因此,也可能需要手動修改這些配置檔案。

Spring和GraalVM本機映像

從Spring Framework 5.1 開始,提供了對GraalVM本機映像的初始支援。 5.2開發週期的重點是改進整合和全面支援,而不需要額外的配置或變通辦法,這是即將到來的Spring Framework 5.3發行版的主題之一

在spring-graal-nativeGithub上庫展示瞭如何從Spring啟動應用程式構建本地映象的例子。該專案實施了GraalFeature,在配置反射,代理等方面承擔了繁重的工作。

看看Spring Boot帶有Tomcat的Spring MVC示例:請記住,在撰寫本文時,該示例期望您正在使用GraalVM 19.2.1,並且已native-image安裝外掛。在構建示例之前,我們需要編譯Spring Graal Feature。github儲存庫根目錄具有一個bash指令碼來執行此操作。

$ ./build-feature.sh 
完成後,讓我們轉到Spring MVC example資料夾並執行compile.sh。它使用Maven構建Spring應用程式,然後生成GraalVM本機映像。該native-image命令隨Spring Graal功能部件的位置以及各種配置檔案一起提供。請注意,本機映像生成比常規Maven構建花費的時間要長得多。另外,該程式喜歡使用大量RAM。完成後,導航到該target資料夾並啟動應用程式

 

$ ./springmvc-tomcat
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::

...

INFO: Started TomcatApplication in 0.054 seconds (JVM running for 0.057)

 注意0.054秒的快速啟動時間。為了進行比較,在JVM中執行應用程式時,報告的啟動時間為1.455秒。

總結

GraalVM本機映像使我們能夠構建提前編譯的JVM應用程式,這些應用程式啟動速度非常快,並且使用的記憶體更少。這對於短暫的過程絕對是有用的,尤其是在無伺服器的情況下(按毫秒計費)。

由於類路徑掃描和自動配置,Spring Boot應用程式在啟動期間非常佔用CPU。當在共享主機上同時啟動多個Spring Boot應用程式時,它們開始爭奪CPU,啟動時間增加。編排工具甚至可能終止程式,因為它們啟動得不夠快。快速啟動的提前編譯的Spring Boot應用程式可能是該問題的答案。

容器化的Spring Boot應用程式也會有所收穫。由於本機二進位制檔案具有所需的一切,因此不再需要將JRE烘焙到容器中。我們可以構建較小的Docker映像

一些以微服務為重點的框架已經利用了本機影象功能(例如QuarkusMicronautHelidon)。儘管Spring Boot尚未完全支援本機映像生成,但我認為它將是該框架的重要補充。 

相關文章