SpringBoot 3.3.5 試用CRaC,啟動速度提升3到10倍
今天和小伙伴們來聊一個稍微新一點的技術話題---CRaC。
CRaC(Coordinated Restore at Checkpoint,檢查點協調恢復)是一個 OpenJDK 項目,旨在解決 Java 應用程序啟動和預熱時間過長的問題。
Java 應用程序啟動和預熱時間過長是一個老大難的問題,目前來看各方也都提出了一些不同的解決思路,之前松哥和大家聊過的 AOT 也能從一定程度上解決啟動慢的問題,今天的 CRaC 算是另外一種解決思路。
一、CRaC是什么
CRaC 允許對運行中的 JVM 進行“快照”,并將其狀態(包括應用)存儲到磁盤中。
之后,在另一個時間點,可以將 JVM 從保存的檢查點恢復到內存中。
這個功能意味著你可以啟動應用程序、預熱并創建檢查點,然后從這個檢查點快速恢復,從而顯著減少啟動時間。
二、CRaC的原理
CRaC 的工作原理基于用戶空間檢查點和恢復(CRIU),這是一個為 Linux 實現檢查點和恢復功能的項目。
CRIU 允許凍結容器或單個應用程序并從保存的檢查點文件中恢復它。
CRaC 采用了 CRIU 的通用方法,并增加了一些增強和調整,使其適用于 Java 應用程序。
一般來說,CRaC 的執行步驟如下:
- 創建檢查點:在應用程序運行并達到穩定狀態后,可以創建一個檢查點,這個檢查點包含了 JVM 的狀態和應用程序的數據。
- 存儲檢查點:檢查點數據被存儲到磁盤上,以便之后可以從中恢復。
- 恢復檢查點:當需要啟動應用程序時,可以直接從檢查點恢復,而不是從頭開始啟動和預熱 JVM。
這個感覺就有點類似于大伙平時使用的 VMWare 的快照功能,在某個時間點為系統拍攝一個快照,下次可以直接從快照啟動,就比從頭開始啟動要快很多。CRaC 所拍快照中不僅包含 JVM,也可以包含你的應用信息。
三、CRaC 的應用場景
CRaC 特別適用于需要快速啟動和恢復的場景,比如:
- 云原生環境:在微服務和無服務器架構中,服務可能需要頻繁地啟動和停止,CRaC 可以顯著減少服務的啟動時間。
- 開發和測試環境:開發者可以在開發和測試過程中快速恢復應用程序到某個已知狀態,提高開發效率。
- 災難恢復:在系統發生故障時,可以快速從最近的檢查點恢復服務,減少系統停機時間。
四、支持版本
從 Spring Boot3.2/Spring6.1 開始對 CRaC 的提供支持,所以如果大伙想體驗 CRaC,需要選擇合適的 SpringBoot 版本。
同時,由于前文提到的 CRaC 依賴于 Linux 特有的 CRIU,因此 CRaC 目前僅在Linux操作系統上支持。Windows 和 Mac 則不支持。
五、實踐
首先我們需要安裝支持 CRaC 的 JDK,目前主要有以下兩種 JDK 支持 CRaC:
- Azul Zulu 21.0.1 + CRaC 版本支持 CRaC,適用于 x64 和 aarch64 CPU 架構,包括 JDK17 和 JDK21。
選擇支持 CRaC 的 JDK
- Liberica JDK 17 和 Liberica JDK 21 提供了對 CRaC 的支持。
接下來在項目中添加 CRaC 依賴:
<dependency>
<groupId>org.crac</groupId>
<artifactId>crac</artifactId>
<version>1.5.0</version>
</dependency>
OK,如此之后,我們的準備工作就算完成了。
接下來我們需要在項目啟動的時候,指定檢查點的位置,并給出生成檢查點的時機:
java -Dspring.context.checkpoint=onRefresh -XX:CRaCCheckpointTo=./tmp_checkpoint -jar javaboy-crac-3.3.5.jar
在上面的啟動腳本中,我們通過設置 JVM 系統屬性 -Dspring.context.checkpoint=onRefresh 來啟用自動檢查點。這個屬性會在 Spring 的 LifecycleProcessor.onRefresh 階段自動創建檢查點,這個階段在所有非延遲初始化的 Singleton 實例化和 InitializingBean#afterPropertiesSet 回調調用之后,但在生命周期啟動和 ContextRefreshedEvent 發布之前。也就是說在這個時機創建檢查點(拍攝快照)。
當然,如果你想等應用程序完全啟動之后再拍攝快照,也是可以的。
先用如下命令啟動應用程序:
java -XX:CRaCCheckpointTo=./tmp_checkpoint -jar javaboy-crac-3.3.5.jar
等待應用程序完全啟動后,在另一個終端執行以下命令來手動觸發檢查點:
jcmd <pid> JDK.checkpoint
其中 <pid> 是應用程序的進程ID,這將創建檢查點并關閉應用程序。檢查點文件將存儲在指定的文件夾中。
手動執行檢查點生成的好處是,這個檢查點包含了框架代碼和應用程序代碼,因此啟動速度會更快,因為框架已經加載并啟動了應用程序。
無論哪種方式生成檢查點,只要有了檢查點,最后一步就是使用這個檢查點了。
我們可以利用檢查點生成的文件來快速拉起應用程序,相關命令如下:
java -XX:CRaCRestoreFrom=./tmp_checkpoint
總結下就是,自動檢查點適合快速實現和無需代碼更改的場景,而手動檢查點提供了更大的靈活性,允許在應用程序完全預熱后創建檢查點,從而可能實現更快的啟動時間。
好啦,感興趣的小伙伴可以去嘗試下,記得選擇合適的操作系統、JDK 版本以及 Spring Boot 版本哦~