刷盤機制
同步刷盤
代碼實現
寫入線程
寫入線程可能同時有多個,但是刷盤線程至始至終就是一個單線程
刷盤線程,始終是操作雙緩沖區域,一個用來刷盤,另一個用來接收多個寫入線程同時寫入刷盤請求
刷盤線程
通過這種方式,實現一次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