大家好!
今天聊一下RocketMQ的文件过期删除机制
本章概括
源码定位
Broker是RocketMQ的核心,提供了消息的接收,存储,拉取等功能
我们可以先从Broker服务入手。从源码可以得知。RocketMQ启用了一个 BrokerController 的 start 函数
public static void main(String[] args) {
start(createBrokerController(args));
}
public static BrokerController start(BrokerController controller) {
try {
controller.start();
String tip = "The broker[";
if (null != controller.getBrokerConfig().getNamesrvAddr()) {
// 日志拼接
}
log.info(tip);
System.out.printf("%s%n", tip);
return controller;
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
复制代码
下列是start 函数启动的异步线程,他启动了一个 messageStore
public void start() throws Exception {
if (this.messageStore != null) {
this.messageStore.start();
}
}
复制代码
从 messageStore.start() 函数进入后会有一个消息存储的第三方接口。
public interface MessageStore {
/**
* Load previously stored messages.
*
* @return true if success; false otherwise.
*/
boolean load();
/**
* Launch this message store.
*
* @throws Exception if there is any error.
*/
void start() throws Exception;
}
复制代码
继续围绕 start 函数展开实现类查找,可以看到最终由 DefaultMessageStore 实现类实现
定位到具体问题之后,可以看到 start 调用了一个 addScheduleTask 函数
这个函数主要处理的就是清除过期日志服务。
public void start() throws Exception {
//刷新ConsumeQueue的服务启动
this.flushConsumeQueueService.start();
//CommitLog刷新的服务启动
this.commitLog.start();
//存储状态检测的服务启动
this.storeStatsService.start();
//创建临时文件,来表示是否正常关机
this.createTempFile();
//启动其他服务。比如清除过期日志的服务等
this.addScheduleTask();
this.shutdown = false;
}
复制代码
这篇文件聊的就是这个 addScheduleTask 函数。言归正传,步入正题!
流程图
过期删除机制
文件过期删除
首次执行时间是60000毫秒=60秒。其余间隔执行都是每10秒执行一次删除。
// 资源回收间隔
private int cleanResourceInterval = 10000;
/**
* {}要执行的任务
* 1.延迟第一次执行的时间
* 2.两次执行之间的时间 10000 资源回收间隔
* 3.毫秒
*/
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
DefaultMessageStore.this.cleanFilesPeriodically();
}
// 延迟第一次执行的时间
}, 1000 * 60, this.messageStoreConfig.getCleanResourceInterval(), TimeUnit.MILLISECONDS);
复制代码
对于删除过期的时机包括以下3种:
- 默认凌晨4点。这个也比较好理解,这个时候用的人也比较少,删除对系统的影响就降到最小。
- 磁盘空间不足。当磁盘空间不足的时候,就要删除过期文件以提供更多的空间出来接收消息。
- 人工触发,指人为的介入去删除。
删除的文件是过期文件,那哪些文件是过期的呢?
首先是保留时间,默认72小时,也就是3天,超过3天的数据,是需要删除的。
deleteExpiredFiles 是用于删除过期文件。执行步骤如下:
- 首先是需要判断是否需要删除文件,通过两个方法的调用isTimeToDelete和isSpaceToDelete判断是否达到定时删除时间以及是否磁盘已满需要删除,以及判断属性DefaultMessageStore.CleanCommitLogService#manualDeleteFileSeveralTimes是否大于0意味着需要手动删除。如果这三个条件任意为真,意味着需要执行删除,那就继续后续的流程。否则结束当前方法。
- 如果是手动删除,则属性DefaultMessageStore.CleanCommitLogService#manualDeleteFileSeveralTimes减1.
- 如果属性MessageStoreConfig#cleanFileForciblyEnable和DefaultMessageStore.CleanCommitLogService#cleanImmediately为真,声明cleanAtOnece为true,否则为false。
- 调用方法 CommitLog#deleteExpiredFile 进行文件删除。方法需要4个入参,分别是: expiredTime:过期时间或者说文件删除前的保留时间,默认为72小时。 deleteFilesInterval:文件删除间隔,这里取值为100. intervalForcibly:该参数用于强制文件强制释放时间间隔,单位是毫秒。这里取值为120*1000, cleanImmediately:是否立即执行删除,这边使用的就是步骤3中的数据。
/**
* 删除已经失效的
*/
private void deleteExpiredFiles() {
int deleteCount = 0;
// 文件保留时长 72
long fileReservedTime = DefaultMessageStore.this.getMessageStoreConfig().getFileReservedTime();
// 100
int deletePhysicFilesInterval = DefaultMessageStore.this.getMessageStoreConfig().getDeleteCommitLogFilesInterval();
// 1000 * 120 = 120000毫秒 = 120秒
int destroyMapedFileIntervalForcibly = DefaultMessageStore.this.getMessageStoreConfig().getDestroyMapedFileIntervalForcibly();
// 判断有没到凌晨4点
boolean timeup = this.isTimeToDelete();
// 空间是否上限
boolean spacefull = this.isSpaceToDelete();
// 手动删除 经过20次的调度
boolean manualDelete = this.manualDeleteFileSeveralTimes > 0;
if (timeup || spacefull || manualDelete) {
if (manualDelete)
this.manualDeleteFileSeveralTimes--;
boolean cleanAtOnce = DefaultMessageStore.this.ge