rabbitmq集群和鏡像隊列


Rabbitmq集群和鏡像隊列

1引言

1.1編寫目的

2 原理和使用

2.1鏡像隊列原理

2.1.1 原理

    默認的一個rabbitmq中的queue是在一個node上的,至於在那個node上取決於client 進行declared的時候的順序,於此不同的是rabbitmq中的exchangebindings都是需要在全部的節點上存在的,也就是rabbitmq集群天生就是支持自動同步這些信息的。而queue則是可以通過mirrored 同步到多個節點上,至於到底是幾個節點可以指定。

    其實rabbitmq集群中的queue不是沒有同步,是有同步的,只是他們同步的只是queue的定義部分或者說聲明部分,並沒有同步queue里面的msg,要想實現也同步queue里面的msg 就需要使用鏡像隊列了

    每一個queue都會創建在一個master node或者 多個slaves node ,並且當master 丟失的時候,最老的slaves node將會變成最新的master,當然這有一個前提那就是要求slave node必須已經同步了master node的內容,如果沒有同步的話那么這個slave node是不可以成為master node

2.1.2 rabbitmq的集群和鏡像隊列區別

    rabbitmq的集群是根據erlang的同步機制來實現的,這是erlang自帶的功能,十分強大,如果幾個rabbitmq node進行了集群后,這些node 之前是可以同步 元數據的,包括exchange的元數據、隊列的元數據、binding的元數據,當時需要注意的是不會包括同步queue的內容默認情況下。主要是因為如果同步的queue的內容的話,如果出現了queue很多的時候,並且每一個queue的內容都很大的時候,rabbitmq就僅僅需要忙着處理這些本node的queue還要負責處理和不同的node之前進行同步,這會造成系統很大負載,對rabbitmq的整體性能會有很多的影響,因此默認是關閉的。那么如果你非要進行隊列的內容同步,比如你的隊列吞吐量並不高,每一個隊列的內容也不大,對性能不會造成什么影響,那么就可以打開鏡像隊列,這個時候集群之前就會同步queue的內容了。

    鏡像隊列其實就是原來channel只會按照binding來把消息路由給某個隊列,現在如果你使用了鏡像隊列,那么channel就需要並行的把消息路由給master node 同時還需要把消息同步給slave node上,所以當使用鏡像隊列以后性能是一定會下降的

    另外說下rabbitmq的集群node中必須有一個要配置為磁盤類型,這個是防止數據丟失的,另外如果整個集群都停掉了那么應該要保證最后一個down掉的node首先被啟動,如果不可以則要使用forget_cluster_node命令將最后關閉的那個node移出集群,如果集群中的節點幾乎同時以不可控的方式down了,此時在其中一個節點使用force_boot命令重啟節點

    另個rabbitmq集群重用的命令:reset 可以把一個node從一個集群中移出,forget_cluster_node則是可以在另一個節點上把某一個node移出自己的集群

2.1.4 queue的master和slave node

    每一個queue都有一個home node也叫主隊列。所有的鏡像操作首先必須要通過這個master,然后在復制到其他的mirrors上,這樣做主要是為了保證消息的FIFO順序,也就是說所有順序執行的操作都要順序交給master,然后channel在同步給slave node,如果master上移出了隊列中的內容,那么所有的slave node也同步的移出所有的隊列內容。但是要記住consumer消費的只是 master node,並不是slave node

    可以通過如下的策略來決定 queue master 在那個節點上:

  • 選擇存在主隊列 最少的的節點: min-masters
  • 選擇client 聲明queue 連接的節點:client-local
  • 隨機選擇:random

 

以上策略的配置可以通過如下幾個方法:

  • 在創建queue的時候使用x-queue-master-locator來指明用上面的那個策略
  • 在配置文件中使用配置項 queue_master_locator指明使用上面的某個策略

 

2.1.3 鏡像隊列的配置使用

    早期的rabbitmq版本要想配置隊列使用使用mirror queue,必須要consumer創建隊列的時候,使用參數來指明。這種方法的不好之處在於不能從rabbitmq server端統一進行配置管理。后來進行了改進,rabbitmq通過policy來激活鏡像隊列,policy可以在任何時候進行修改,並且修改后立即生效,比如你可以在任何時候把一個非鏡像隊列通過policy修改成一個鏡像隊列,反這亦然。另外當一個鏡像隊列只有一個master 沒有slaves的時候,他和非鏡像隊列也是不一樣的,雖然看起來是一樣的,相比較而言非鏡像隊列的性能要好很多

下面說下鏡像隊列的通過policy方法進行配置的方法

為了讓一個queue或者對個queue變成mirror queue 必須要產生一個policy,這個policy會使用表達式語法和隊列的名字進行匹配,並且會使用policykeyha-mode 和可選的ha-params 決定怎么同步queue

ha-mode 

ha-params 

結果

all 

 

所有node都要復制queue,並且當集群中添加一個新的node的時候,queue也會復制過去

extactly

count

queue只會同步到ha-params中指定的countnode上,如果集群中的node個數小於countqueue將會同步到整個集群中,如果集群中的node個數大於count,當一個node包含一個mirror queue並且down的時候,一個新的mirror將會被創建

nodes

node names

消息被mirror到指定的node上通過指定node namenode nameerlang node name 可以通過 rabbitmqctl cluster_status 查看,一般的形式是rabbit@hostname

不管什么時候發生了鏡像隊列的 policy 變化,rabbitmq都會盡力保持現有的mirror 但是同時要和新的policy要盡量符合

下面是幾個例子:

  • 所有以ha.開頭的隊列並鏡像到集群中的所有其他隊列中

rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}'

  • 所有以two.開頭的隊列並鏡像到集群中任意兩個node

rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}' 

  • 所有以test.開頭的隊列並鏡像到集群中指明名字的兩個node

rabbitmqctl set_policy ha-nodes "^nodes\." \ 

 '{"ha-mode":"nodes","ha-params":["rabbit@nodeA", "rabbit@nodeB"]} 

2.1.5 nodes policy 對queue master的影響

如果在改變鏡像隊列的 policy的時候 選擇了 nodes 類型的policy 這個有可能會造成

原來存在的queue master 消失,如果這個queue master 所在的node 不在 nodes list內。為了阻止queue master的丟失,rabbitmq將會保持queue master 存在直到slaves開始同步。不過一旦同步開始,那么rabbitmq 就不會繼續保證這個 queue master 還需要保持了,這個時候consumer就會失去和queue master的連接,然后需要重新連接。所以這就是說一旦queue master down,那么consumer就一定會出現一次reconnection,只是在reconnection之前你有沒有包裝數據的同步

例如 如果一個queue is on [A,B] 其中 node A上是 queue master ,此時如果你創建了一個policy 類型的 nodes 告訴 rabbitmq 消息需要在node [C,D] 那么在初始階段 queue是在[A,C,D]三個node上,但是一旦同步那么就queue就會在[C,D]上,master A queue 將會被關閉.所以是要手動同步還是要自動同步,也需要我們仔細考慮

2.1.6 排他性隊列(exclusive queues)

    排他性隊列是一種和connection生命周期關聯的隊列,一旦connection關閉那么排他性隊列就會被刪除,可以想想下如果一個node crash了 那么和這個node 建立的connection都會被關閉所以排他性隊列也會被刪除,因此這樣的隊列不應該被mirror

2.2鏡像隊列使用

2.2.1 新節點同步機制

    如果集群中新加了一個node,那么這個node就會變成備復制節點,而且備復制節點不會主動的復制master node 上的queue的內容,當然了他是會同步master node上的queue的元數據的,那么怎么實現和master node上的queue內容同步呢,這就需要隨着時間的推移一點一點的來同步了,也就是說新加的節點只會同步后來加入到master node上的隊列內容並且隨着master node上的queue里面的內容被不斷的消費掉,很快備份節點和master node上面的內容就會一樣了。

    但是考慮到萬一consumer正好發布消息到了新的node上,並且master node 又掛了會怎么樣呢,答案是會丟失消息。因此在master node 將現存的queue的內容復制到其他的新節點之前,分辨是否所有從copy和master node擁有相同的內容就很重要了。為了檢測鏡像隊列同步的狀態,可以使用如下的命令來確定:

rabbitmqctl list_queues pid slave_pids synchronised_slave_pids

這個命令會顯示出 slave-pids 和synchronised_slave_pids

如果兩個是一樣的時候就表示所有的隊列內容同步完成了否則就是還沒有同步完成

這個時候master node是一定不可以刪除的

2.2.2 停止所有node后slave刪除隊列內容

    當所有node停止后再重啟,如果之前有聲明為持久化的隊列,那么master node上會繼續保留有之前queue的全部信息和內容,但是slave node只會同步到queue的元數據,並不會同步到queue的內容,主要是slave node 重啟后並不知道自己原來保留的queue的內容是否和master node上的queue的內容一致,所以干脆就刪除全部的消息內容,感覺就相當於一個新的node剛剛加入一個集群一樣

 

2.2.3 停止只有未同步的slave的master node

    集群中可能會出現所有的slave node都未同步完成的時候,master被關閉了,雖然這種情況出現的可能性不會太大,但是是有這個可能的。這個關閉可能是下面兩個情況:

    有意的或者說可控的關閉了master node,比如有意的停止rabbitmq或者關閉了服務器,這個時候為了避免消息丟失,rabbitmq不會同步fail over 到其他的還未同步完成的slave上,相反的整個queue將會被stop掉,給人的感覺就好像是slave node根本不存在一樣

    無意的不是人為的,而是由於rabbitmq crash了或者突然掉電了,這個是會出發master node 自動 fail over 到未同步完成的slave node上

    如果實在是想即使slave node沒同步,mater node關閉后也要fail over到slave node上那么可以使用rabbitmq的policy:設置policy中的ha-promote-on-shutdown的默認值when-synced改為always

2.2.4 所有slave 關閉時候 master 異常關閉

    這種情況更少見,主要是slave 都處於關閉狀態時候,master node 不知道什么原因不在集群中了,一般情況下,slave都關閉了,master丟失的時候,根據上一個場景"停止只有未同步的slave的master node" 我們可以知道這個時候slave如果沒有同步就被關閉了那么master消失后,queue也沒有了master。但是如果我們執行 forget_cluster_node 移出master的時候,rabbitmq會試圖為master node上每一個隊列找到一個當前被stop的slave node,並在這個slave node重啟后被提升為master,如果有多個slave node可以選擇,那么最近關閉的slave node會被選中

    這個需要理解為什么我們只能選擇stop的slave node,而不是運行的slave node呢?答案其實在場景"停止所有node后消息同步"中說的一樣,一旦關閉了slave node重啟后就會自動刪除所有的本地queue的內容了

 

 


免責聲明!

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



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