RabbitMQ鏡像隊列原理分析


1. 創建鏡像隊列模式

注意,到此步驟,我們假設是你已經創建好了RabbitMQ集群。

1.1 增加鏡像隊列的Policy

打開你的RabbitMQ管理首頁,在Admin->Policy鏈接下開始創建Policy:
image

  • Name: 你配置的Policy名稱;
  • Pattern: 匹配模式,圖片的是匹配testMirror開頭的交換機和隊列;
  • Priority: 優先級;
  • Definition: 一些模式的定義,RabbitMQ已經列出了以下常見的模式定義。ha-mode是指定鏡像隊列的模式,有效值為all/exactly/nodes。其中all表示在集群中所有的節點上進行鏡像;exactly表示在指定個數的節點上進行鏡像,節點的個數由ha-params指定;nodes表示在指定的節點上進行鏡像,節點名稱通過ha-params指定。ha-sync-mode為指定鏡像隊列中消息的同步方式,有效值為automatic(自動同步),manually(手動同步);默認是manually,請注意一定要記得設置為automatic(自動同步),否則消息在鏡像隊列中是不會自動同步的(即普通集群模式),即新節點加入時不會自動同步消息和元數據,只能通過命令手動去同步。

1.2 代碼聲明交換機、隊列、綁定等

image

1.3 查看鏡像隊列是否聲明成功

查看你剛聲明的隊列的詳情:
image

可以看到,ha-mode、node、salve等屬性都已經創建成功。

2. 鏡像隊列說明

2.1 關於node節點

queue有master節點和slave節點。 要強調的是,在RabbitMQ中master和slave是針對一個queue而言的,而不是一個node作為所有queue的master,其它node作為slave。一個queue第一次創建的node為它的master節點,其它node為slave節點。

2.2 鏡像隊列服務提供方式

image
如上圖所示,在鏡像隊列集群模式中,對某個queue來說,只有master對外提供服務,而其他slave只提供備份服務,在master所在節點不可用時,選出一個slave作為新的master繼續對外提供服務。

無論客戶端的請求打到master還是slave最終數據都是從master節點獲取。當請求打到master節點時,master節點直接將消息返回給client,同時master節點會通過GM(Guaranteed Multicast)協議將queue的最新狀態廣播到slave節點。GM保證了廣播消息的原子性,即要么都更新要么都不更新。

當請求打到slave節點時,slave節點需要將請求先重定向到master節點,master節點將將消息返回給client,同時master節點會通過GM協議將queue的最新狀態廣播到slave節點。

所以,多個客戶端連接不同的鏡像隊列不會產生同一message被多次接受的情況。

2.3 RabbitMQ集群處理新增節點

如果有新節點加入,RabbitMQ不會同步之前的歷史數據,新節點只會復制該節點加入到集群之后新增的消息。
既然master節點退出集群會選一個slave作為master,那么如果不幸選中了一個剛剛加入集群的節點怎么辦?那消息不就丟了嗎?這里您可以把心放到肚子里,RabbitMQ集群內部會維護節點的狀態是否已經同步,使用rabbitmqctl的synchronised_slave_pids參數,就可以查看狀態。如果slave_pids和synchronised_slave_pids里面的節點是一致的,那說明全都同步了;如果不一致很容易比較出來哪些還沒有同步,集群只會在“最老”的slave節點之間選一個出來作為新的master節點。另外對於node節點的重啟也是按照新節點來處理的。

2.4 鏡像隊列注意點

  • 鏡像隊列不能作為負載均衡使用,因為每個聲明和消息操作都要在所有節點復制一遍。
  • ha-mode參數和durable declare對exclusive隊列都不生效,因為exclusive隊列是連接獨占的,當連接斷開,隊列自動刪除。所以實際上這兩個參數對exclusive隊列沒有意義。
  • 每當一個節點加入或者重新加入(例如從網絡分區中恢復回來)鏡像隊列,之前保存的隊列內容會被清空。
  • 對於鏡像隊列,客戶端basic.publish操作會同步到所有節點;而其他操作則是通過master中轉,再由master將操作作用於salve。比如一個basic.get操作,假如客戶端與slave建立了TCP連接,首先是slave將basic.get請求發送至master,由master備好數據,返回至slave,投遞給消費者。

2.5 鏡像隊列的故障恢復

假設兩個節點(A和B)組成一個鏡像隊列。

  • 場景1:A先停,B后停。 該場景下B是master(disk節點,A是ram),只要先啟動B,再啟動A即可。或者先啟動A,再在30秒之內啟動B即可恢復鏡像隊列。
  • 場景2: A, B同時停。 該場景可能是由掉電等原因造成,只需在30秒之內連續啟動A和B即可恢復鏡像隊列。
  • 場景3:A先停,B后停,且A無法恢復。 該場景是場景1的加強版,因為B是master,所以等B起來后,在B節點上調用rabbitmqctl forget_cluster_node A,解除與A的cluster關系,再將新的slave節點加入B即可重新恢復鏡像隊列。
  • 場景4:A先停,B后停,且B無法恢復。 該場景是場景3的加強版,比較難處理,早在3.1.x時代之前貌似都沒什么好的解決方法,但是現在已經有解決方法了,在3.4.2版本親測有效(我們當前使用的是3.3.5)。因為B是master,所以直接啟動A是不行的,當A無法啟動時,也就沒辦法在A節點上調用rabbitmqctl forget_cluster_node B了。新版本中,forget_cluster_node支持–offline參數,offline參數允許rabbitmqctl在離線節點上執行forget_cluster_node命令,迫使RabbitMQ在未啟動的slave節點中選擇一個作為master。當在A節點執行rabbitmqctl forget_cluster_node –offline B時,RabbitMQ會mock一個節點代表A,執行forget_cluster_node命令將B剔出cluster,然后A就能正常啟動了。最后將新的slave節點加入A即可重新恢復鏡像隊列。
  • 場景5: A先停,B后停,且A、B均無法恢復,但是能得到A或B的磁盤文件。 該場景是場景4的加強版,更加難處理。將A或B的數據庫文件(默認在$RABBIT_HOME/var/lib目錄中)拷貝至新節點C的目錄下,再將C的hostname改成A或B的hostname。如果拷過來的是A節點磁盤文件,按場景4處理方式;如果拷過來的是B節點磁盤文件,按場景3處理方式。最后將新的slave節點加入C即可重新恢復鏡像隊列。
  • 場景6:A先停,B后停,且A、B均無法恢復,且無法得到A或B的磁盤文件。 洗洗睡吧,該場景下已無法恢復A、B隊列中的內容了。

以上內容就是RabbitMQ鏡像隊列原理分析的全部內容了,謝謝你閱讀到了這里!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM