RocketMQ最新版個人學習代碼註釋筆記

個人源碼學習:https://gitee.com/wangen2009/RocketMQ.git

註釋格式:註釋2.3.3

broker:broker 模塊

client:消息客戶端,包含消息生產者、消息消費者相關類

common:公共包

dev:開發者信息(非源代碼 )

distribution:部署實例文件夾

example:示例代碼

filter:消息過濾相關基礎類

filtersrv:消息過濾服務器實現相關類

openmessaging:消息開放標準

remoting:遠程通信模塊,基於 Netty

srvutil:服務器工具類

store:消息存儲實現相關類

style:checkstyle 相關實現

test:測試相關類

tools:工具類,監控命令相關實現類

1.3.1 設計理念

基於主題的發佈與訂閱模式:消息發送、消息存儲、消息消費 NameServer 實現元數據的管理(Topic路由信息等),但集羣之間互不通信

2 路由中心 NameServer

2.1 架構設計

NameServer 互相之間不通信,Broker 消息服務器在啓動時向所有的 NameServer 註冊,Producer 在發送消息之前從 NameServer 獲取 Broker 服務器地址列表,然後根據負載算法選擇一臺進行發送, NameServer 與每臺 Broker 保持長連接,30s 檢測 Broker 是否存活,如果檢測到 Broker 宕機, 從路由註冊表中移除,但不會馬上通知 Producer,爲了降低 NameServer 的複雜度,讓 Producer 的容錯機制保證消息發送的高可用性

2.3 路由註冊、故障剔除

2.3.1 路由元信息

QueueData:在 2M-2S 中,每個 M-S 的每個 Topic 有 4個讀隊列和4個寫隊列 BrokerData BrokerLiveInfo

2.3.2 路由註冊

Broker 基於定時線程池組裝請求信息,遍歷 NameServer 發送,多個 Broker 心跳包 NameServer 基於讀寫鎖串行更新 Broker 信息,但是讀取 Broker 表信息是併發讀,標準 的 ReadWriteLock

2.3.3 路由刪除

被動:NameServer 的定時任務線程會 10s 掃描一次 Broker 元信息表,查看 BrokerLive 的 lastUpdateTimestamp 並與當前時間戳對比,超過 120s 進行移除操作,並更新對應的其它 元信息表 主動:Broker 正常關閉,發送 unregisterBroker 指令刪除

2.3.4 路由發現

非實時,即 NameServer 不主動推送最新的路由,而是由客戶端主動定時通過特殊的某個主題去拉 取最新的路由

3 消息發送

可靠消息發送、可靠異步發送、單向(oneway)發送

3.1 簡略消息發送

同步(sync):發送消息的API是阻塞的,直到消息服務器返回 異步(async):發送消息的API是異步主線程不阻塞,通過回調來獲取發送結果 單向(oneway):發送消息的API直接返回,也沒回調函數,只管發

3.2 Message 類

擴展都存放到 map,包括 tag keys 等

3.3 生產者啓動流程

3.3.1 DefaultMQProducer

3.3.2 生產者啓動流程

3.4 消息發送基本流程

驗證消息、查找路由、消息發送

3.4.1 消息長度驗證

3.4.2 查找主題路由信息

3.4.3 選擇消息隊列

根據 ThreadLocal + random + volatile:對消息隊列輪詢查找,並對超過閾值 broker 沒有 交互的直接剔除,避免輪詢已經宕機的 broker 消息隊列

3.4.4 消息發送

1、NameServer 掛機

在發送消息階段,如果生產者本地緩存中沒有緩存 topic 的路由信息,則需要從 NameServer 獲取,只有當所有 NameServer 都不可用時,此時會拋 MQClientException。如果所有的 NameServer 全部掛掉,並且生產者有緩存 Topic 的路由信息,此時依然可以發送消息。所以,NameServer 的宕機,通常不會對整個消息發送帶來什麼嚴重的問題。

2、Broker掛機

基礎知識:消息生產者每隔 30s 從 NameServer 處獲取最新的 Broker 存活信息(topic路由信息),Broker 每30s 向所有的 NameServer 報告自己的情況,故 Broker 的 down 機,Procuder 的最大可感知時間爲 60s,在這 60s,消息發送會有什麼影響呢?

此時分兩種情況分別進行分析。

1)啓用sendLatencyFaultEnable

由於使用了故障延遲機制,詳細原理見上文詳解,會對獲取的 MQ 進行可用性驗證,比如獲取一個MessageQueue 發送失敗,這時會對該 Broker 進行標記,標記該 Broker 在未來的某段時間內不會被選擇到,默認爲(5分鐘,不可改變),所有此時只有當該 topic 全部的 broker 掛掉,才無法發送消息,符合高可用設計。

2)不啓用sendLatencyFaultEnable = false

此時會出現消息發送失敗的情況,因爲默認情況下,procuder 每次發送消息,會採取輪詢機制取下一個 MessageQueue,由於可能該 Message 所在的Broker掛掉,會拋出異常。因爲一個 Broker 默認爲一個 topic 分配4個 messageQueue,由於默認只重試2次,故消息有可能發送成功,有可能發送失敗。

3.5 批量消息發送

使用 MessageBatch 類,在發送時使用 instanceof 判斷是否是批量,從而進行操作 壓縮不支持批量

4 消息存儲

4.1 存儲概要

Commitlog、ConsumeQueue、IndexFile

Commitlog:將所有主題的消息存儲在同一個文件中,確保消息發送時順序寫文件,但是對消息主題檢索消息 不友好 因此額外增加了 ConsumeQueue 消息隊列文件,每個消息主題包含多個消息消費隊列, 每個消息隊列有一個消息文件 IndexFile 索引文件,加速消息檢索,根據消息的屬性快速從 Commitlog 檢索消息

4.2 初識消息存儲

4.3 消息發送存儲流程

4.4 存儲文件組織與內存映射

CommitLog、ConsumeQueue、IndexFile:單個文件固定長度,文件名爲該文件第一條消息對應的全局物理偏移量

4.4.1 MappedFileQueue 映射文件隊列

MappedFileQueue 是對存儲目錄的封裝,即作爲一個文件夾,下面包含多個文件

4.4.2 MappedFile 內存映射文件

勘誤:第96頁,不是 MappedFile 的 shutdown 而應該是 ReferenceResource 的 shutdown, 其中使用了引用計數關閉

4.4.3 TransientStorePool

短暫的存儲池,用來臨時存儲數據,數據先寫入該內存映射中,然後由 commit 線程定時將數據從該 內存複製到與目的物理文件對應的內存映射中 引入該機制主要原因是提供一種內存鎖定,將當前堆外內存一直鎖定在內存中,避免被進程將內存交換到磁盤。

4.5 RocketMQ存儲文件

commitlog:消息存儲目錄 config:運行時配置 consumerFilter.json:主題消息過濾信息 consumerOffset.json:集羣消費模式消息消費進度 delayOffset.json:延時消息隊列拉取進度 subscriptionGroup.json:消息消費組配置信息 topics.json:topic配置屬性 consumequeue:消息消費隊列存儲目錄 index:消息索引文件存儲目錄 abort:如果存在,則說明 Broker 非正常關閉,啓動時創建,正常退出前刪除 checkpoint:存儲 commitlog 文件最後一次刷盤時間戳 、consumequeue 最後一次刷盤時間、index 索引文件最後一次刷盤時間戳

4.5.1 Commitlog文件

4.5.2 ConsumeQueue文件

同一主題的消息不連續地存儲在 commitlog 文件中,如果需要查找某個主題下的消息,只能通過遍歷, 效率極低,因此設計了消息消費隊列文件(ConsumeQueue),即作爲 Commitlog 文件消息消費的"索引" 文件,consumequeue 的第一級目錄爲消息主題,第二級目錄爲主題的消息隊列

Topic1 -------- 0 -------- 1 -------- 2 -------- 3 Topic2 -------- 0 -------- 1 -------- 2 -------- 3 4.5.3 Index索引文件

4.6 實時更新消息消費隊列與索引文件

消息消費隊列文件、消息屬性屬性文件都是基於 CommitLog 文件構建,當消息生產者提交的消息存儲在 CommitLog 文件中, ConsumeQueue、IndexFile 需要及時更新,否則消息無法及時被消費,而且查找消息也出現延遲 RocketMQ 通過開啓一個線程 ReputMessageService 來準實時轉發 CommitLog 文件更新事件(事件通知)

4.6.1 根據消息更新 ConsumeQueue

寫入到 mappedFile,默認異步落盤

4.6.2 根據消息更新 Index 索引文件

4.7 消息隊列與索引文件恢復

問題:消息成功存儲到 Commitlog,但是轉發任務未執行,Broker 宕機,此時三個文件不一致 啓動時通過判斷 abort 文件的存在從而判斷是否是異常宕機,初始化文件的偏移量,進行文件恢復

4.7.1 Broker 正常停止文件恢復

4.7.2 Broker 異常停止文件恢復

4.8 文件刷盤機制

基於 JDK NIO 的 MappedByteBuffer,內存映射機制,先將消息追加到內存,然後根據刷盤策略 在不同的時間寫入磁盤,如果是同步刷盤,消息追加到內存後,將同步調用 MappedByteBuffer.force(), 如果是異步刷盤,則追加到內存後立刻返回給消息發送端 基於 Commitlog 文件刷盤機制分析,其它兩個文件類似

4.8.1 Broker 同步刷盤

消息生產者在消息服務端將消息內容追加到內存映射文件後(內存),需要同步將內存的內容立刻 刷寫到磁盤,其中是堆外內存保證零拷貝效率,MappedByteBuffer.force 保證寫入

4.8.2 Broker 異步刷盤

將消息直接追加到 ByteBuffer(Direct),wrotePostion 隨着消息追加而移動 CommitRealTimeService 線程每200ms將Buffer新追加的內容的數據提交到MappedByteBuffer FlushRealTimeService 線程每500ms將MappedByteBuffer中新追加的內存通過調用 MappedByteBuffer.force 寫入到磁盤 4.9 過期文件刪除機制 超過72小時的兩個文件,無論消息是否被消費,都被刪除

4.10 總結

消息到達 commitlog 通過定時線程轉發到 consumequeue/indexFile 使用 abort 文件做異常宕機的記錄

5 消息消費

5.1 消息消費概述

集羣模式、廣播模式 推模式、拉模式

5.2 消息消費者初探

5.3 消費者啓動流程

5.4 消息拉取

5.4.1 PullMessageService 實現機制

如何實現:一個消息隊列在同一時間只允許被一個消息消費者消息,一個消息小飛飛着可以同時消費多個消息隊列

5.4.2 ProcessQueue 實現機制

ProcessQueue 是 MessageQueue 在消費端的重現、快照?

PullMessageService 從消息服務器默認每次拉取32條消息,按消息的隊列偏移量順序存放在 ProcessQueue 中 PullMessageService 然後將消息提交到消費者消費線程池,消息成功消費後從 ProcessQueue 中移除

5.4.3 消息拉取基本流程

消息拉取看客戶端消息拉取請求封裝

消息服務器查找並返回消息

消息拉取客戶端處理返回的消息

通過長輪詢向消息服務端發送拉取請求,如果消息消費者向 RocketMQ 發送消息拉取時,消息並未到達 消費隊列,如果不啓用長輪詢機制,則會在服務端等待 shortPollingTimeMills 時間後(掛起) 再去判斷消息是否已達到消息隊列,如果消息未到達則提示消息拉取客戶端不存在。 如果開啓了長輪詢模式,RocketMQ 以方便會每 5s 輪詢檢查一次消息是否可達,同時一有新消息達到 後立馬通知掛起線程再次驗證新消息是否是自己感興趣的消息,如果是則從 commitlog 文件提取消息 返回給消息拉取客戶端,否則直到掛起超時,push超時時間默認 15s。pull模式通過

5.5 消息隊列負載與重新分佈機制

消息隊列重新分佈通過 RebalanceService 線程實現

5.6 消息消費過程

消息拉取:PullMessageService 負責懟消息隊列進行消息拉取,從遠端服務器拉取消息後將 消息存入 ProcessQueue 消息隊列處理隊列中,然後調用 ConsumeMessageService.submitConsumeRequest() 進行方法消費,消息拉取與消息消費解耦

5.6.1 消息消費

5.6.2 消息確認(ACK)

消息監聽器返回的消息結果爲 RECONSUME_LATER,則需要將這些消息發送給 Broker 延遲消息。 如果發送 ACK 消息失敗,將延遲 5s 後提交線程池進行消費

5.6.3 消費進度管理

消息消費者在消費一批消息後,需要記錄該批消息已經消費完畢,即消息進度文件

廣播模式:同一個消費組的所有消息都需要消費主體下的所有消息,即消息進度需要獨立存儲,最 理想的存儲地方應該是與消費者綁定

集羣模式:同一個消費組內的所有消息消費者共享消息主題下的所有消息,所以消費進度需要保存 在一個每個消費者都能訪問到的地方

5.7 定時消息機制

消息發送到 Broker 後,等到特定的時間後才能被消費,RocketMQ 並不支持任意的時間精度, 如果要支持任意時間精度的定時調度,不可避免地需要在 Broker 層做消息排序(ScheduledExecutorService), 再加上持久化方面的考量,將不可避免地帶來巨大的性能消耗,所以 RocketMQ 只支持特定級別的延遲消息, 在 Broker 端通過 messageDelayLevel 配置:默認爲 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h,delayLevel=1 表示延遲1s。 定時任務,前面的消息重試也是藉助的定時任務實現,在將消息存入 commitlog 文件之前需要判斷 消息的重試次數,如果大於0,則會將消息的主題設置爲 SCHEDULE_TOPIC_XXXX

5.7.1 load 方法

5.7.2 start 方法

5.7.3 定時調度邏輯

爲每個延遲級別創建一個調度任務,每一個延遲級別對應 SCHEDULE_TOPIC_XXXX 主題下的一個 消息消費隊列

整體流程:

消息消費者發送消息,如果發送消息的 delayLevel 大於0,則改變消息主題爲 SCHEDULE_TOPIC_XXXX, 消息隊列爲 delayLevel-1 消息經由 commitlog 轉發消息消費隊列 定時任務每隔1s 根據上次拉取偏移量從消費隊列中取出所有消息 根據消息的物理偏移量與消息大小從 commitlog 中拉取消息 根據消息屬性重新創建消息,存入 commitlog 文件 轉發到原 Topic 的消息消費隊列,供消息消費者消費

5.8 消息過濾機制

提交一個過濾雷到 FilterServer,消息消費者從 FilterServer 拉取消息。表達式紛紛爲 TAG 與 SQL92 表達式 commitlog 存儲的是消息的 tag 的hashcode,直接對比 hashcode

5.9 順序消費 支持局部消息順序消費,即確保同一個消息消費隊列中的消息被順序消費,如果需要做到全局順序 消費則考科一將主題匹配成一個隊列,例如數據庫 BinLog 等要求嚴格順序的場景

5.9.1 消息隊列負載 需要先通過 RebalanceService 線程實現消息隊列的負載,集羣模式下同一個消費組內的消費者 共同承擔其訂閱主題下消息隊列的消費,同一個消息消費隊列在同一時刻只會被消費組內一個消費者消費, 一個消費者同一時刻可以分配多個消費隊列 拉取任務時需要在 Broker 服務器鎖定該消息隊列

5.9.2 消息拉取

5.9.3 消息消費

當一個新的消費隊列分配給消費者時,在添加其拉取任務之前必須向 Broker 發送對該消息隊列加鎖請求

5.9.4 消息隊列鎖實現

5.10 總結

消息隊列負載20s一次

消息拉取線程默認一次批量拉取32條消息,會有消息拉取流控可控制

不支持任意精度的定時調度,延遲級別是有固定的消息消費隊列主題來支持

順序消費一般使用集羣模式時,必須鎖定消息消費隊列,在 Broker 端會存儲消息消費隊列的鎖佔用情況

6 消息過濾 FilterServer

6.1 ClassFilter 運行機制

Broker 進程所在的服務器會啓動多個 FilterServer 進程 消費者在訂閱消息主題時會上傳一個自定義的消息過濾實現類,FilterServer加載並實例化 消息消費者向FilterServer發送消息拉取請求,FilterServer接收到消息消費者消息拉取請求後, FilterServer將消息拉取請求轉發給 Broker,Broker返回消息後在 FilterServer端執行消息過濾 邏輯,然後返回消息

6.2 FilterServer 註冊剖析

6.3 類過濾模式訂閱機制

6.4 消息拉取 消息拉取時,判斷消息過濾模式是否爲 classFilter,將拉取消息服務器地址由原來的 Broker 地址轉換成該 Broker 服務器所對應的 FilterServer

7 RocketMQ 主從同步(HA)機制

HAService:主從同步核心實現類

HAService$AcceptSocketService:HA Master 端監聽客戶端連接實現類

HAService$GroupTransferService:主從同步通知實現類

HAService$HAClient:HA Client端實現類

HAConnection:HA Master 服務端HA 連接對象的封裝,與 Broker 從服務器的網絡讀寫實現類

HAConnection$ReadSocketService:HA Master 網絡讀實現類

HAConnection$WriteSockketService:HA Master 網絡寫實現類

7.1 主從複製原理

7.1.1 HAService 整體工作機制

從服務器主動連接主服務器,主服務器接收客戶端的連接

從服務器主動向主服務器發送待拉取消息偏移量,主服務器解析請求並返回消息給從服務器

從服務器保存消息並繼續發發送新的消息同步請求

7.1.2 AcceptSocketService 實現原理

標準NIO的服務端請求,選擇器每1s處理一次連接就緒事件,HAConnection將負責M-S數據同步邏輯

7.1.3 GroupTransferService 實現原理

同步阻塞實現,即M-S-sync 消息發送者將消息刷寫到磁盤後,需要繼續等待新數據被傳輸到從服務器,從服務器數據的複製 是在另一個線程 HAConnection 中拉去,消息發送者在這裏需要等待數據傳輸的結果

7.1.4 HAClient 實現原理

7.1.5 HAConnection 實現原理

7.2 讀寫分離機制

同一組Broker(M-S)服務器,它們的brokerName相同但brokerId不同,主爲0,從>0,

7.3 本章小結

HA 核心是實現是從服務器在啓動的時候主動向主服務器建立 TCP長連接,獲取服務器的 commitlog 最大偏移量,以此偏移量向主服務器主動拉取消息,循環進行,達到主從服務器數據同步 讀寫分離:消費者先向主服務器發起拉取請求,然後主服務器返回一批消息,並根據主服務器負載壓力 與主從同步情況,向從服務器(勘誤:消費者服務器)建議下次消息拉取是從主服務器還是從從服務器拉取

勘誤:224頁,從服務器應該改爲向消費者服務器

8 事務消息

8.1 事務消息實現思想

基於兩階段提交和定時事務狀態回查決定消息最終是 commit/rollback

8.2 事務消息發送流程

8.3 提交或回滾你事務

第二階段:提交或回滾事務 提交或回滾成功後,原消息不會物理刪除,而是修改 Topic 邏輯刪除

8.4 事務消息回查事務狀態

通過 TransactionalMessageCheckService 線程池定時檢測 RMQ_SYS_TRANS_ HALF_TOPIC 消息, 用戶回查消息的事務狀態

8.5 本章小結

事務消息基於兩階段提交和定時任務事務狀態回查機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章