JVM 檢查點/恢復

Spring Framework 與 Project CRaC 實現的檢查點/恢復功能整合,以便透過 JVM 減少基於 Spring 的 Java 應用的啟動和預熱時間。

使用此功能需要滿足以下條件:

  • 支援檢查點/恢復的 JVM(目前僅限 Linux)。

  • 類路徑中存在 org.crac:crac 庫(支援 1.4.0 及以上版本)。

  • 指定所需的 Java 命令列引數,例如 -XX:CRaCCheckpointTo=PATH-XX:CRaCRestoreFrom=PATH

當請求建立檢查點時,在 -XX:CRaCCheckpointTo=PATH 指定的路徑中生成的檔案包含執行中 JVM 記憶體的表示,其中可能包含秘密或其他敏感資料。使用此功能時,應假設 JVM "看到" 的任何值(例如來自環境的配置屬性)都將儲存在這些 CRaC 檔案中。因此,應仔細評估這些檔案生成、儲存和訪問位置與方式的安全影響。

從概念上講,檢查點和恢復與單個 Bean 的 Spring Lifecycle 契約一致。

執行中應用的按需檢查點/恢復

可以按需建立檢查點,例如使用 jcmd application.jar JDK.checkpoint 等命令。建立檢查點之前,Spring 會停止所有正在執行的 Bean,透過實現 Lifecycle.stop 使它們有機會在需要時關閉資源。恢復後,相同的 Bean 會重新啟動,透過 Lifecycle.start 允許 Bean 在相關時重新開啟資源。對於不依賴 Spring 的庫,可以透過實現 org.crac.Resource 並註冊相關例項來提供自定義的檢查點/恢復整合。

利用執行中應用的檢查點/恢復通常需要額外的生命週期管理,以便優雅地停止和開始使用檔案或套接字等資源,並停止活動執行緒。
請注意,當以固定速率定義排程任務時(例如使用 `@Scheduled(fixedRate = 5000)` 註解),透過按需檢查點/恢復恢復 JVM 後,檢查點和恢復之間所有錯過的執行都將執行。如果這不是您想要的行為,建議以固定延遲(例如使用 `@Scheduled(fixedDelay = 5000)`)或 cron 表示式排程任務,因為這些任務在每次執行後計算。
如果在已預熱的 JVM 上建立檢查點,恢復的 JVM 也將同樣預熱,從而可能立即達到峰值效能。此方法通常需要訪問遠端服務,因此需要一定程度的平臺整合。

啟動時的自動檢查點/恢復

設定 -Dspring.context.checkpoint=onRefresh JVM 系統屬性後,將在啟動時 LifecycleProcessor.onRefresh 階段自動建立檢查點。此階段完成後,所有非延遲初始化的單例都已例項化,並且已呼叫 InitializingBean#afterPropertiesSet 回撥;但生命週期尚未開始,ContextRefreshedEvent 尚未釋出。

出於測試目的,也可以利用 -Dspring.context.exit=onRefresh JVM 系統屬性,它會觸發類似的行為,但不是建立檢查點,而是在相同的生命週期階段退出 Spring 應用,無需 Project CraC 依賴/JVM 或 Linux。這對於檢查 Bean 未啟動時是否需要連線遠端服務非常有用,並可能最佳化配置以避免這種情況。

如上所述,特別是在 CRaC 檔案作為可部署 artifact(例如容器映象)一部分交付的使用場景中,應假設 JVM "看到" 的任何敏感資料最終都會存入 CRaC 檔案中,並仔細評估相關的安全影響。
自動檢查點/恢復是一種將應用啟動“快進”到應用上下文即將啟動階段的方式,但它不能實現完全預熱的 JVM。