刷盘机制
同步刷盘
代码实现
写入线程
写入线程可能同时有多个,但是刷盘线程至始至终就是一个单线程
刷盘线程,始终是操作双缓冲区域,一个用来刷盘,另一个用来接收多个写入线程同时写入刷盘请求
刷盘线程
通过这种方式,实现一次flush操作,能把多个写入线程同时写入到MappedFile中的多条消息,一次性的刷入磁盘,也就是实现了“批量刷盘”的效果
异步刷盘
这里一共有三种线程,FlushRealTimeService,CommitRealTimeService,broker写入主线程。CommitLog这个类负责来协调这三种线程,这三种线程沟通的桥梁,就是MappedFileQueue中的lastMappedFile。
broker写入主线程,和CommitRealTimeService负责往桥梁中写入消息并唤醒FlushRealTimeService线程,而FlushRealTimeService线程被前面两个线程唤醒后,就去将桥梁中新写入的消息flush到磁盘上去。
三个线程具体是如何协调的:
开启了堆外内存后,broker写入主线程会把消息写入到从堆外内存池借来的byteBuffer中,再由CommitRealTimeService线程,通过当前要写入的MappedFile的fileChannel字段,调用fileChannel.write(byteBuffer)来将byteBuffer中的消息,写入PageCache。CommitRealTimeService写入完成后,就调用FlushRealTimeService#wakeup()唤醒FlushRealTimeService线程,FlushRealTimeService线程,最后去将桥梁中新写入的消息flush到磁盘上去。
没开启堆外内存时,broker写入主线程会直接把消息写入到当前MappedFile的mappedByteBuffer字段中(也就是直接写入了PageCache中)。写入完成后,broker写入主线程就调用FlushRealTimeService#wakeup()唤醒FlushRealTimeService线程,FlushRealTimeService线程,最后去将桥梁中新写入的消息flush到磁盘上去。
一般有两种,有两种方式进行读写
(1)第一种,Mmap+PageCache的方式,读写消息都走的是pageCache,这样子读写都在pagecache里面不可避免会有锁的问题,在并发的读写操作情况下,会出现缺页中断降低,内存加锁,污染页的回写。
(2)第二种,DirectByteBuffer(堆外内存)+PageCache的两层架构方式,这样子可以实现读写消息分离,写入消息时候写到的是DirectByteBuffer——堆外内存中,读消息走的是PageCache(对于DirectByteBuffer是两步刷盘,一步是刷到PageCache,还有一步是刷到磁盘文件中),带来的好处就是,避免了内存操作的很多容易堵的地方,降低了时延,比如说缺页中断降低,内存加锁,污染页的回写。
视频地址:04-RocketMQ刷盘机制_哔哩哔哩_bilibili
面试题部分
头条面试:RocketMQ为何默认使用mmap,并且可配置成FileChannel异步发送消息
注意上面mmap的方式就一个参数,每500ms,将pagecache中的数据刷入磁盘一次
fileChannel的方式有三个参数,200ms从堆外内存刷到pagecache一次、200ms或者4个pagecache页满了就从pagecache刷入磁盘一次
两种方式从写入速度上进行的对比
因为rocketmq从最开始就是定位为业务处理MQ,大多数时候都是发送小数据,所以rocketmq默认就是使用的mmap的方式。但是rocketmq也借鉴并保留了kafka的方式的fileChannel的方式,因为kafka只要用在大数据场景
s
视频地址:头条面试:RocketMQ为何默认使用mmap,并且可配置成FileChannel异步发送消息_哔哩哔哩_bilibili