1 引文
在CommitLog中,有mappedFileQueue属性,该queue中最后一个mappedFile是当前正在顺序写的对象;刷盘服务flushCommitLogService为CommitLog另一个属性,包含同步罗盘和异步罗盘。
异步落盘的逻辑为在FlushRealTimeService服务中的run方法里有个死循环,跳出条件为stopped == true,具体流程如下:
1.读取配置中的刷新间隔时长,线程休息指定时间
2.获取配置中的最少刷盘页数,默认是4页,只有脏数据达到"指定”页数后,才真正刷盘。
3获取强制刷盘周期,默认是10秒,达到强制刷盘周期后,一定会刷盘,不再考虑脏页大小
4.调用外部类CommitLog.mappedFileQueue.flush(…)进行刷盘。注意传递的参数:int flushPhysicQueueLeastPages;(当达到强刷周期后该值为0,没有则是4,即默认的4页)
在死循环结束后,会尝试10次刷盘,直到无数据被落盘后结束。
CommitLog.mappedFileQueue.flush(…)会调用mappedByteBuffer.force(),该方法是系统调用,将没有落盘的数据落盘。
异步刷盘不需要等到全部落盘后再返回,只要唤醒FlushRealTimeService服务线程就可以了。简单介绍完异步落盘后开始介绍今天的主角:同步落盘。
2 同步落盘
同步落盘中,调用存储消息的线程必须要等到asyncPutMessage(msg)方法消息存储到磁盘后才能返回,当生产者线程调用该方法后并不是亲力亲为自己将消息落盘,而是在GroupCommitService类中的线程去完成的,下图展示了两者在存储消息上的区别。其中,1~7步为将消息存储到MappedFile中的逻辑;构建请求的GroupCommitRequest实例中nextOffset表示存储完消息后下一条消息开始的offset,flushOKFuture.get()方法会阻塞线程,生产者线程将其创建完毕之后会调用GroupCommitService的putRequest方法加入到GroupCommitService中的List<GroupCommitRequest> requestsWrite属性中。get方法什么时候会被唤醒呢?1.超时;2.有结果返回后。
GroupCommitService继承自ServiceThread,有自己的线程,putRequest方法有两个作用:
- 将request添加到requestsWrite列表;
- 唤醒刷盘服务。
线程的run方法的流程如下:
waitForRunning方法继承自ServiceThread,并在GroupCommitService中重写了onWaitEnd方法,waitForRunning如下:
GroupCommitService中的onWaitEnd方法调用了swapRequests()方法,该方法的作用为交换requestsWrite和requestsRead。为什么要交换呢?因为requestsWrite还会有数据往里写,防止其受到影响。
doCommit方法的逻辑如下: