在之前的文章中,已經把 Broker、Producer 和 Conusmer 的部分原始碼和核心的機制介紹的差不多了,但是其實 RocketMQ 中還有一個比較關鍵但是我們平時很容易忽略的元件——NameServer。
在日常的使用中,我們接觸的最多的還是 Producer 和 Consumer,而 NameServer 沒有直接跟我們有互動。就像 Kafka 叢集背後用於其叢集後設資料管理的 Zookeeper 叢集一樣,NameServer 也在背後支撐著 RocketMQ 正常工作。
你給翻譯翻譯,什麼叫 NameServer
NameServer 你可以簡單的把它理解成註冊中心。
Broker 啟動的時候會將自己註冊到 NameServer 中,註冊的同時還會將 Broker 的 IP 地址、埠相關的資料,以及儲存在 Broker 中的 RocketMQ 叢集路由的資料一併跟隨心跳傳送到 NameServer。這裡的路由資訊是指 Topic 下的 MessageQueue 分別都在哪臺 Broker 上。
而 Producer 則會從 NameServer 中獲取後設資料,從而將 Message 發到對應的 Broker 中去。
相應的,Consumer 也需要從 NameServer 中獲取資料。平常我們配置消費者,裡面重要的資訊主要就兩個,分別是你要消費的 Topic 和當前的 Consumer Group。根據配置,Consumer 會去 NameServer 獲取對應的 Topic 都有哪些 Broker,其真實的 IP 地址和埠是多少,拿到了這個之後就可以開始進行訊息消費了。
註冊 Broker 都做了什麼
這裡我們先通過註冊 Broker 的原始碼來預熱一下,為後面閱讀整個部分的原始碼做準備,直接上程式碼。
首先這裡做了一個對 Broker 版本的區分,不同的版本採用不同的處理方式,鑑於官網現在最新的版本都已經到了 4.9.0
了,就暫時先不考慮低版本的情況了,後面有時間再討論。
只有向上面那種幾行的程式碼會給大家貼出來,其餘的程式碼我會盡量用流程圖代替
校驗 Body 的完整性
首先是校驗 Broker 傳過來的資料的完整性。很簡單的一個判斷,將 Broker 傳過來的 Body 用 CRC32演算法 加密之後,和請求中 Header 中所帶的由 Broker 加密的值進行對比,不同的話就說明資料的完整性出了問題,接下來需要中斷註冊流程。
解析Body
這裡分成兩種情況:
Body為空 Body不為空
如果 Body 為空,則會將當前要註冊的 Broker 的 DataVersion 給重置;
而 **Body 不為空 **則會進行對 Body 進行解析,主要是從中解析出 DataVersion
,代表 Broker 中的資料版本。其次解析出這個 Broker 中儲存的所有 Topic 及其相關的配置。
執行註冊邏輯
這裡就是註冊的核心邏輯了,這裡為了更加容易理解,我們來分情況討論,就不把兩種情況揉在一起了。
首次註冊 非首次註冊
維護叢集中 Broker 的 Name
在整個操作開始之前,會先給 RouteInfoManager
加一把鎖,這個 RouteInfoManager
裡面就是 NameServer 儲存資料的地方。這個鎖是個讀寫鎖,使用的是 Java 中的 ReentrantReadWriteLock
。
這裡的 BrokerName 是在 RocketMQ 配置檔案中配置的變數。就是用於標識一個 Broker 的名字,但我們知道 Broker 是有主從架構的,並且 RocketMQ 4.5 之後推出的 Dleger 可以實現一主多從,換句話說,一個 Broker Name 可能會對應多個 Broker 例項。
在 MQ 看來,Broker 是多例項部署的;而在 Producer 或者 Consumer 來看,Broker就只有一個。所以,這個步驟內所維護的就是在當前叢集中,有多少個這樣的 Broker Name。
維護 Broker 的資料
然後,RocketMQ 會在 brokerAddrTable
中維護每個 Broker 的核心資料,包含:
Broker 所處的叢集 Broker 的名字(上面剛剛討論過) 所有 Broker 的 BrokerID 和 Address 的對應關係,是個 Map,Address 為 IP+埠
同一個 Broker Name 下,為什麼會有多個地址資訊已經在上個步驟解答過,不在此贅述。
Broker 的資料維護主要有兩個方面:
該 Broker 資料在 brokerAddrTable
中是否存在brokerAddrTable
中維護的資料不能有重複的地址資訊
第一個過於基礎簡單,就不再贅述。我們重點看第二個點,我們知道會有多個 Broker 地址,存在一個 Map 中,因為 Broker 是基於主從架構。那不知道你有沒有想過,NameServer 如何區分 主 和 從 的呢?
答案是通過 Map 的 Key,如果是 0
則代表是 Master 節點,1
則代表 Slave 節點,因為 RocketMQ 自己實現的 Broker 主從架構是一主一從,而一主多從則是由 RocketMQ 4.5 之後加入的 Dleger 實現的,暫時先不討論。區分的邏輯如下圖:
那什麼時候會出現重複呢?
答案是主從切換
舉個例子,假設某個 Slave Broker 的 Address 為 192.168.1.101:8081
,且已經註冊。此時brokerAddrs
中已經有一個key: 1 value: 192.168.1.101:8081
記錄了。
當叢集中的 Master 當機之後,會進行故障恢復,假設選中了上面這個 Broker 為新的 Master,在進行註冊的時候會發現,brokerAddrs
中已經有一個同樣的 Address 了,只是 Key 不同。但是由於它們從本質上來說就是同一臺機器,如果不將 key 為1,也就是角色為 Slave 的記錄去掉,就會造成資料一致性的問題。
簡單總結一下來說,同一個 Adreess,在 brokerAddrs
中只能存在一個。感興趣的可以看一下原始碼,其實跟上面文字描述的邏輯是一樣的。
去除了重複的 Address 資料之後,就會將本次註冊的 Broker 的資料註冊進 brokerAddrs
中。
維護 MessageQueue 的資料
這裡主要是根據 Broker 的資料更新其 MessageQueue 相關的資料。接下來,我們詳細解析一下 Message Queue 的維護流程,同樣會給出原始碼和流程圖,兩部分等價,可選擇性觀看。
當 Master 節點來註冊時,如果是首次註冊或者資料有更新,便會呼叫一個方法createAndUpdateQueueData
去維護 MessageQueue 相關的資料。這裡對資料是否更新的判斷,是基於 DataVersion
的,代表 Broker 資料的版本。
此後通過 Topic 的 Name 拿到對應的 MessageQueue 的列表,這裡可能會有點疑問,一個 Topic 難道不應該只有一個對 MessageQueue 相關的配置嗎,為什麼這裡拿到的是個列表?
小了,格局小了
Topic 是個邏輯上的概念,一個 Topic 的 MessageQueue 會分佈在不同的 Broker 上,所有這裡是個列表。
更新的流程如上圖,拿到了 MessageQueue 的列表之後,會和本次註冊的 Broker 中的 MessageQueue 資料做一個對比,如果發現不同就進行全量的替換,沒什麼其他的複雜對比邏輯。原始碼等同上圖,感興趣的可以自行檢視。
維護 Broker 的存活資訊
到這裡,MessageQueue 相關的邏輯就處理完了,接下來 NameServer 會再去更新 brokerLiveTable
中的資料,這裡存放了當前正在活躍的所有 Broker。這塊的作用後續會講。
NameServer 啟動流程
上面通過了解註冊 Broker的整個流程,對整個 NameServer 的架構有了個大概的瞭解,接下來再從整體視角來看一下 NameServer。
整體的流程上面這張圖已經給出來了,就不放原始碼了,意義不大。
這裡說一下掃描不再活躍的Broker,這個後臺執行緒會每 10秒 鍾執行一次,這裡會對上文提到的 brokerLiveTable
進行遍歷處理,因為這裡面維護了所有的正在活躍的 Broker。
如果某個 Broker 超過了 120秒 沒有傳送心跳給 NameServer,就會將其從 brokerLiveTable
中移除。
NameServer 可處理的操作
上面簡單瞭解了 註冊 Broker 的流程,實際上 NameServer 還支援很多其他的操作,這裡就不再這裡列出來了,看了沒有意義,感興趣的可以自己去網上找,一大堆的資料。而且 Register Broker
這個操作中所涉及到原始碼中的資料結構,其他的操作都會用到,所以瞭解了 Register Broker
之後,再去閱讀其他操作的原始碼會非常的順。
好了以上就是本篇部落格的全部內容了,歡迎微信搜尋關注【SH的全棧筆記】,回覆【佇列】獲取MQ學習資料,包含基礎概念解析和RocketMQ詳細的原始碼解析,持續更新中。
如果你覺得這篇文章對你有幫助,還麻煩點個贊,關個注,分個享,留個言。