NameServer 核心原理解析
本文轉(zhuǎn)載自微信公眾號(hào)「SH的全棧筆記」,作者SH的全棧筆記。轉(zhuǎn)載本文請(qǐng)聯(lián)系SH的全棧筆記公眾號(hào)。
在之前的文章中,已經(jīng)把 Broker、Producer 和 Conusmer 的部分源碼和核心的機(jī)制介紹的差不多了,但是其實(shí) RocketMQ 中還有一個(gè)比較關(guān)鍵但是我們平時(shí)很容易忽略的組件——NameServer。
在日常的使用中,我們接觸的最多的還是 Producer 和 Consumer,而 NameServer 沒(méi)有直接跟我們有交互。就像 Kafka 集群背后用于其集群元數(shù)據(jù)管理的 Zookeeper 集群一樣,NameServer 也在背后支撐著 RocketMQ 正常工作。
你給翻譯翻譯,什么叫 NameServer
NameServer 你可以簡(jiǎn)單的把它理解成注冊(cè)中心。
Broker 啟動(dòng)的時(shí)候會(huì)將自己注冊(cè)到 NameServer 中,注冊(cè)的同時(shí)還會(huì)將 Broker 的 IP 地址、端口相關(guān)的數(shù)據(jù),以及保存在 Broker 中的 RocketMQ 集群路由的數(shù)據(jù)一并跟隨心跳發(fā)送到 NameServer。這里的路由信息是指 Topic 下的 MessageQueue 分別都在哪臺(tái) Broker 上。
而 Producer 則會(huì)從 NameServer 中獲取元數(shù)據(jù),從而將 Message 發(fā)到對(duì)應(yīng)的 Broker 中去。
相應(yīng)的,Consumer 也需要從 NameServer 中獲取數(shù)據(jù)。平常我們配置消費(fèi)者,里面重要的信息主要就兩個(gè),分別是你要消費(fèi)的 Topic 和當(dāng)前的 Consumer Group。根據(jù)配置,Consumer 會(huì)去 NameServer 獲取對(duì)應(yīng)的 Topic 都有哪些 Broker,其真實(shí)的 IP 地址和端口是多少,拿到了這個(gè)之后就可以開(kāi)始進(jìn)行消息消費(fèi)了。
注冊(cè) Broker 都做了什么
這里我們先通過(guò)注冊(cè) Broker 的源碼來(lái)預(yù)熱一下,為后面閱讀整個(gè)部分的源碼做準(zhǔn)備,直接上代碼。
首先這里做了一個(gè)對(duì) Broker 版本的區(qū)分,不同的版本采用不同的處理方式,鑒于官網(wǎng)現(xiàn)在最新的版本都已經(jīng)到了 4.9.0 了,就暫時(shí)先不考慮低版本的情況了,后面有時(shí)間再討論。
只有向上面那種幾行的代碼會(huì)給大家貼出來(lái),其余的代碼我會(huì)盡量用流程圖代替
校驗(yàn) Body 的完整性
首先是校驗(yàn) Broker 傳過(guò)來(lái)的數(shù)據(jù)的完整性。很簡(jiǎn)單的一個(gè)判斷,將 Broker 傳過(guò)來(lái)的 Body 用 CRC32算法 加密之后,和請(qǐng)求中 Header 中所帶的由 Broker 加密的值進(jìn)行對(duì)比,不同的話就說(shuō)明數(shù)據(jù)的完整性出了問(wèn)題,接下來(lái)需要中斷注冊(cè)流程。
解析Body
這里分成兩種情況:
- Body為空
- Body不為空
如果 Body 為空,則會(huì)將當(dāng)前要注冊(cè)的 Broker 的 DataVersion 給重置;
而 **Body 不為空 **則會(huì)進(jìn)行對(duì) Body 進(jìn)行解析,主要是從中解析出 DataVersion ,代表 Broker 中的數(shù)據(jù)版本。其次解析出這個(gè) Broker 中存儲(chǔ)的所有 Topic 及其相關(guān)的配置。
執(zhí)行注冊(cè)邏輯
這里就是注冊(cè)的核心邏輯了,這里為了更加容易理解,我們來(lái)分情況討論,就不把兩種情況揉在一起了。
- 首次注冊(cè)
- 非首次注冊(cè)
維護(hù)集群中 Broker 的 Name
在整個(gè)操作開(kāi)始之前,會(huì)先給 RouteInfoManager 加一把鎖,這個(gè) RouteInfoManager 里面就是 NameServer 存儲(chǔ)數(shù)據(jù)的地方。這個(gè)鎖是個(gè)讀寫(xiě)鎖,使用的是 Java 中的 ReentrantReadWriteLock。
這里的 BrokerName 是在 RocketMQ 配置文件中配置的變量。就是用于標(biāo)識(shí)一個(gè) Broker 的名字,但我們知道 Broker 是有主從架構(gòu)的,并且 RocketMQ 4.5 之后推出的 Dleger 可以實(shí)現(xiàn)一主多從,換句話說(shuō),一個(gè) Broker Name 可能會(huì)對(duì)應(yīng)多個(gè) Broker 實(shí)例。
在 MQ 看來(lái),Broker 是多實(shí)例部署的;而在 Producer 或者 Consumer 來(lái)看,Broker就只有一個(gè)。所以,這個(gè)步驟內(nèi)所維護(hù)的就是在當(dāng)前集群中,有多少個(gè)這樣的 Broker Name。
維護(hù) Broker 的數(shù)據(jù)
然后,RocketMQ 會(huì)在 brokerAddrTable 中維護(hù)每個(gè) Broker 的核心數(shù)據(jù),包含:
Broker 所處的集群
Broker 的名字(上面剛剛討論過(guò))
所有 Broker 的 BrokerID 和 Address 的對(duì)應(yīng)關(guān)系,是個(gè) Map,Address 為 IP+端口
同一個(gè) Broker Name 下,為什么會(huì)有多個(gè)地址信息已經(jīng)在上個(gè)步驟解答過(guò),不在此贅述。
Broker 的數(shù)據(jù)維護(hù)主要有兩個(gè)方面:
- 該 Broker 數(shù)據(jù)在 brokerAddrTable 中是否存在
- brokerAddrTable 中維護(hù)的數(shù)據(jù)不能有重復(fù)的地址信息
第一個(gè)過(guò)于基礎(chǔ)簡(jiǎn)單,就不再贅述。我們重點(diǎn)看第二個(gè)點(diǎn),我們知道會(huì)有多個(gè) Broker 地址,存在一個(gè) Map 中,因?yàn)?Broker 是基于主從架構(gòu)。那不知道你有沒(méi)有想過(guò),NameServer 如何區(qū)分 主 和 從 的呢?
答案是通過(guò) Map 的 Key,如果是 0 則代表是 Master 節(jié)點(diǎn),1 則代表 Slave 節(jié)點(diǎn),因?yàn)?RocketMQ 自己實(shí)現(xiàn)的 Broker 主從架構(gòu)是一主一從,而一主多從則是由 RocketMQ 4.5 之后加入的 Dleger 實(shí)現(xiàn)的,暫時(shí)先不討論。區(qū)分的邏輯如下圖:
那什么時(shí)候會(huì)出現(xiàn)重復(fù)呢?
答案是主從切換
舉個(gè)例子,假設(shè)某個(gè) Slave Broker 的 Address 為 192.168.1.101:8081 ,且已經(jīng)注冊(cè)。此時(shí)brokerAddrs 中已經(jīng)有一個(gè)key: 1 value: 192.168.1.101:8081 記錄了。
當(dāng)集群中的 Master 宕機(jī)之后,會(huì)進(jìn)行故障恢復(fù),假設(shè)選中了上面這個(gè) Broker 為新的 Master,在進(jìn)行注冊(cè)的時(shí)候會(huì)發(fā)現(xiàn),brokerAddrs 中已經(jīng)有一個(gè)同樣的 Address 了,只是 Key 不同。但是由于它們從本質(zhì)上來(lái)說(shuō)就是同一臺(tái)機(jī)器,如果不將 key 為1,也就是角色為 Slave 的記錄去掉,就會(huì)造成數(shù)據(jù)一致性的問(wèn)題。
簡(jiǎn)單總結(jié)一下來(lái)說(shuō),同一個(gè) Adreess,在 brokerAddrs 中只能存在一個(gè)。感興趣的可以看一下源碼,其實(shí)跟上面文字描述的邏輯是一樣的。
去除了重復(fù)的 Address 數(shù)據(jù)之后,就會(huì)將本次注冊(cè)的 Broker 的數(shù)據(jù)注冊(cè)進(jìn) brokerAddrs 中。
維護(hù) MessageQueue 的數(shù)據(jù)
這里主要是根據(jù) Broker 的數(shù)據(jù)更新其 MessageQueue 相關(guān)的數(shù)據(jù)。接下來(lái),我們?cè)敿?xì)解析一下 Message Queue 的維護(hù)流程,同樣會(huì)給出源碼和流程圖,兩部分等價(jià),可選擇性觀看。
當(dāng) Master 節(jié)點(diǎn)來(lái)注冊(cè)時(shí),如果是首次注冊(cè)或者數(shù)據(jù)有更新,便會(huì)調(diào)用一個(gè)方法createAndUpdateQueueData去維護(hù) MessageQueue 相關(guān)的數(shù)據(jù)。這里對(duì)數(shù)據(jù)是否更新的判斷,是基于 DataVersion 的,代表 Broker 數(shù)據(jù)的版本。
此后通過(guò) Topic 的 Name 拿到對(duì)應(yīng)的 MessageQueue 的列表,這里可能會(huì)有點(diǎn)疑問(wèn),一個(gè) Topic 難道不應(yīng)該只有一個(gè)對(duì) MessageQueue 相關(guān)的配置嗎,為什么這里拿到的是個(gè)列表?
小了,格局小了
Topic 是個(gè)邏輯上的概念,一個(gè) Topic 的 MessageQueue 會(huì)分布在不同的 Broker 上,所有這里是個(gè)列表。
更新的流程如上圖,拿到了 MessageQueue 的列表之后,會(huì)和本次注冊(cè)的 Broker 中的 MessageQueue 數(shù)據(jù)做一個(gè)對(duì)比,如果發(fā)現(xiàn)不同就進(jìn)行全量的替換,沒(méi)什么其他的復(fù)雜對(duì)比邏輯。源碼等同上圖,感興趣的可以自行查看。
維護(hù) Broker 的存活信息
到這里,MessageQueue 相關(guān)的邏輯就處理完了,接下來(lái) NameServer 會(huì)再去更新 brokerLiveTable 中的數(shù)據(jù),這里存放了當(dāng)前正在活躍的所有 Broker。這塊的作用后續(xù)會(huì)講。
NameServer 啟動(dòng)流程
上面通過(guò)了解注冊(cè) Broker的整個(gè)流程,對(duì)整個(gè) NameServer 的架構(gòu)有了個(gè)大概的了解,接下來(lái)再?gòu)恼w視角來(lái)看一下 NameServer。
NameServer的主要流程
整體的流程上面這張圖已經(jīng)給出來(lái)了,就不放源碼了,意義不大。
這里說(shuō)一下掃描不再活躍的Broker,這個(gè)后臺(tái)線程會(huì)每 10秒 鐘執(zhí)行一次,這里會(huì)對(duì)上文提到的 brokerLiveTable 進(jìn)行遍歷處理,因?yàn)檫@里面維護(hù)了所有的正在活躍的 Broker。
如果某個(gè) Broker 超過(guò)了 120秒 沒(méi)有發(fā)送心跳給 NameServer,就會(huì)將其從 brokerLiveTable 中移除。
NameServer 可處理的操作