CRaC(Coordinated Restore at Checkpoint,检查点协调恢复)是一个 OpenJDK 项目,旨在解决 Java 应用程序启动和预热时间过长的问题。
Java 应用程序启动和预热时间过长是一个老大难的问题,目前来看各方也都提出了一些不同的解决思路,之前松哥和大家聊过的 AOT 也能从一定程度上解决启动慢的问题,今天的 CRaC 算是另外一种解决思路。
CRaC 允许对运行中的 JVM 进行“快照”,并将其状态(包括应用)存储到磁盘中。
之后,在另一个时间点,可以将 JVM 从保存的检查点恢复到内存中。
这个功能意味着你可以启动应用程序、预热并创建检查点,然后从这个检查点快速恢复,从而显著减少启动时间。
CRaC 的工作原理基于用户空间检查点和恢复(CRIU),这是一个为 Linux 实现检查点和恢复功能的项目。
CRIU 允许冻结容器或单个应用程序并从保存的检查点文件中恢复它。
CRaC 采用了 CRIU 的通用方法,并增加了一些增强和调整,使其适用于 Java 应用程序。
一般来说,CRaC 的执行步骤如下:
CRaC 特别适用于需要快速启动和恢复的场景,比如:
从 Spring Boot3.2/Spring6.1 开始对 CRaC 的提供支持,所以如果大伙想体验 CRaC,需要选择合适的 SpringBoot 版本。
同时,由于前文提到的 CRaC 依赖于 Linux 特有的 CRIU,因此 CRaC 目前仅在Linux操作系统上支持。Windows 和 Mac 则不支持。
首先我们需要安装支持 CRaC 的 JDK,目前主要有以下两种 JDK 支持 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 版本哦~