RabbitMQ Network Partitions 處理策略

網(wǎng)絡(luò)分區(qū)的意義

RabbitMQ采用的鏡像隊(duì)列是一種環(huán)形的邏輯結(jié)構(gòu),如下圖:



RabbitMQ 除了發(fā)布(Publish)消息之外,所有的其余操作都是在master上完成,之后再將有影響的操作同步到slave節(jié)點(diǎn)上。如果客戶端連接的是slave節(jié)點(diǎn),RabbitMQ機(jī)制也會先將連接路由到master節(jié)點(diǎn)上。比如確認(rèn)(Ack)一條消息,先在A節(jié)點(diǎn)上,即master節(jié)點(diǎn)上確認(rèn),之后再轉(zhuǎn)向B節(jié)點(diǎn),進(jìn)而是C和D節(jié)點(diǎn),最后再D返回Ack之后才真正將這條消息確認(rèn),進(jìn)而標(biāo)記為可刪除。這個種復(fù)制的原理可以保證更強(qiáng)的數(shù)據(jù)一致性,在這種一致性模型下,如果出現(xiàn)網(wǎng)絡(luò)波動或者網(wǎng)絡(luò)延遲等,那么整個復(fù)制鏈的性能就會下降。就以上圖為例,如果C節(jié)點(diǎn)網(wǎng)絡(luò)異常,那么整個A->B->C->D->A的循環(huán)復(fù)制過程就會大受影響,整個RabbitMQ服務(wù)性能將大打折扣,所以這里就需要引入網(wǎng)絡(luò)分區(qū)來將異常的節(jié)點(diǎn)排離出整個分區(qū)之外,以確保整個RabbitMQ的性能。待網(wǎng)絡(luò)情況轉(zhuǎn)好之后再將此節(jié)點(diǎn)加入集群之中。

網(wǎng)絡(luò)分區(qū)的判定

RabbitMQ中與網(wǎng)絡(luò)分區(qū)的判定相關(guān)的是net_ticktime這個參數(shù),默認(rèn)為60s。在RabbitMQ集群中的每個broker節(jié)點(diǎn)會每隔 net_ticktime/4 (默認(rèn)15s)計(jì)一次tick(如果有任何數(shù)據(jù)被寫入節(jié)點(diǎn)中,此節(jié)點(diǎn)被認(rèn)為被ticked),如果在連續(xù)四次某節(jié)點(diǎn)都沒有被ticked到,則判定此節(jié)點(diǎn)處于down的狀態(tài),其余節(jié)點(diǎn)可以將此節(jié)點(diǎn)剝離出當(dāng)前分區(qū)。將連續(xù)四次的tick時間即為T,那么T的取值范圍為 0.75ticktime < T < 1.25ticktime。下圖可以形象的描述出這個取值范圍的原因。



默認(rèn)情況下,在45s<T<75s之間會判定出網(wǎng)絡(luò)分區(qū)。

RabbitMQ會將queues,exchanges,bindings等信息存儲在Erlang的分布式數(shù)據(jù)庫——Mnesia中,許多圍繞網(wǎng)絡(luò)分區(qū)的一些細(xì)節(jié)都和這個Mnesia的行為有關(guān)。如果一個節(jié)點(diǎn)不能在T時間內(nèi)連上另一個節(jié)點(diǎn)(這里的連上特指broker節(jié)點(diǎn)之間的內(nèi)部通信),那么Mnesia通常認(rèn)為這個節(jié)點(diǎn)已經(jīng)down了,就算之后兩個節(jié)點(diǎn)又重新恢復(fù)內(nèi)部通信,但是這兩個節(jié)點(diǎn)都會認(rèn)為對方已經(jīng)down,Mnesia此時認(rèn)定發(fā)生了網(wǎng)絡(luò)分區(qū)的情況。這些會被記錄到RabbitMQ的服務(wù)日志(默認(rèn)在$RABBITMQ_HOME/var/log/rabbitmq/目錄下)之中,如下所示:

=ERROR REPORT==== 16-Jul-2021::15:20:55 ===
Mnesia('rabbit@node1'): ** ERROR ** mnesia_event got {inconsistent_database, running_partitioned_network, 'rabbit@node2'}

當(dāng)一個節(jié)點(diǎn)起來的時候,RabbitMQ會記錄是否發(fā)生了網(wǎng)絡(luò)分區(qū),你可以通過WebUI進(jìn)行查看,后臺會直接紅字提醒"network partitions";或者可以通過rabbitmqctl cluster_status命令查看,如果查看到信息中的partitions那一項(xiàng)是空的,就像這樣:

[{nodes,[{disc,['rabbit@node1', 'rabbit@node2']}]},
 {running_nodes,['rabbit@node2','rabbit@node1']},
 {cluster_name,<<"rabbit@node1">>},
 {partitions,[]}]

然而當(dāng)網(wǎng)絡(luò)分區(qū)時,會變成這樣:

[{nodes,  [{disc,  ['rabbit@node1','rabbit@node2']}]},
 {running_nodes,['rabbit@node1']},
 {cluster_name,<<"rabbit@node1">>},
 {partitions,  [{'rabbit@node1',['rabbit@node2']}]}]

當(dāng)一個RabbitMQ集群發(fā)生網(wǎng)絡(luò)分區(qū)時,這個集群會分成兩個或者多個分區(qū),它們各自為政,互相都認(rèn)為對方分區(qū)的節(jié)點(diǎn)已經(jīng)down,包括queues,bindings,exchanges這些信息的創(chuàng)建和銷毀都處于自身分區(qū)內(nèi),與其它分區(qū)無關(guān)。如果原集群中配置了鏡像隊(duì)列,而這個鏡像隊(duì)列又牽涉到兩個或者多個網(wǎng)絡(luò)分區(qū)中的節(jié)點(diǎn)時,每一個網(wǎng)絡(luò)分區(qū)中都會出現(xiàn)一個master節(jié)點(diǎn),如果分區(qū)節(jié)點(diǎn)個數(shù)充足,也會出現(xiàn)新的slave節(jié)點(diǎn),對于各個網(wǎng)絡(luò)分區(qū),彼此的隊(duì)列都是相互獨(dú)立的,當(dāng)然也會有一些其他未知的、怪異的事情發(fā)生。當(dāng)網(wǎng)絡(luò)恢復(fù)時,網(wǎng)絡(luò)分區(qū)的狀態(tài)還是會保持,除非采取一些措施去解決他。

手動處理網(wǎng)絡(luò)分區(qū)

為了從網(wǎng)絡(luò)分區(qū)中恢復(fù),首先需要挑選一個信任的分區(qū),這個分區(qū)才有決定Mnesia內(nèi)容的權(quán)限,發(fā)生在其他分區(qū)的改變將不被記錄到Mnesia中而直接丟棄。手動恢復(fù)網(wǎng)絡(luò)分區(qū)有兩種思路:

  1. 停止其他分區(qū)中的節(jié)點(diǎn),然后重新啟動這些節(jié)點(diǎn)。最后重啟信任分區(qū)中的節(jié)點(diǎn),以去除告警。
  2. 關(guān)閉整個集群的節(jié)點(diǎn),然后再啟動每一個節(jié)點(diǎn),這里需確保你啟動的第一個節(jié)點(diǎn)在你所信任的分區(qū)之中。

停止/啟動節(jié)點(diǎn)有兩種操作方式:

  1. rabbimqctl stop/ rabbitmq-server -detached
  2. rabbitmqctl stop_app/ rabbitmqctl start_app

自動處理網(wǎng)絡(luò)分區(qū)

RabbitMQ提供了4種處理網(wǎng)絡(luò)分區(qū)的方式,在rabbitmq.config中配置cluster_partition_handling參數(shù)即可,分別為:

  1. ignore
  2. pause_minority
  3. pause_if_all_down, [nodes], ignore|autoheal
  4. autoheal

1. ignore

默認(rèn)是ignore,如果不配置rabbitmq.config或者按如下配置:

 [
        {
                rabbit, [
                        {cluster_partition_handling, ignore}
                ]

        }
 ].

ignore的配置是當(dāng)網(wǎng)絡(luò)分區(qū)的時候,RabbitMQ不會自動做任何處理,即需要手動處理。

2. pause_minority

在rabbitmq.config配置文件中配置:

 [
        {
                rabbit, [
                        {cluster_partition_handling, pause_minority}
                ]
        }
 ].

當(dāng)發(fā)生網(wǎng)絡(luò)分區(qū)時,集群中的節(jié)點(diǎn)在觀察到某些節(jié)點(diǎn)down掉時,會自動檢測其自身是否處于少數(shù)派(小于或者等于集群中一般的節(jié)點(diǎn)數(shù))。少數(shù)派中的節(jié)點(diǎn)在分區(qū)發(fā)生時會自動關(guān)閉,當(dāng)分區(qū)結(jié)束時又會啟動。這里的關(guān)閉是指RabbitMQ application關(guān)閉,而Erlang VM并不關(guān)閉,這個類似于執(zhí)行了rabbitmqctl stop_app命令。處于關(guān)閉的節(jié)點(diǎn)會每秒檢測一次是否可連通到剩余集群中,如果可以則啟動自身的應(yīng)用,相當(dāng)于執(zhí)行rabbitmqctl start_app命令。這種處理方式適合集群節(jié)點(diǎn)數(shù)大于2個且最好為奇數(shù)的情況。

3. pause_if_all_down

在pause_if_all_down模式下,RabbitMQ會自動關(guān)閉不能和list中節(jié)點(diǎn)通信的節(jié)點(diǎn)。語法為{pause_if_all_down, [nodes], ignore|autoheal},其中[nodes]即為前面所說的list。如果一個節(jié)點(diǎn)與list中的所有節(jié)點(diǎn)都無法通信時,自關(guān)閉其自身。如果list中的所有節(jié)點(diǎn)都down時,其余節(jié)點(diǎn)如果是ok的話,也會根據(jù)這個規(guī)則去關(guān)閉其自身,此時集群中所有的節(jié)點(diǎn)會關(guān)閉。如果某節(jié)點(diǎn)能夠與list中的節(jié)點(diǎn)恢復(fù)通信,那么會啟動其自身的RabbitMQ應(yīng)用,慢慢的集群可以恢復(fù)。

有兩種配置如下:

 [
        {
                rabbit, [
                        {cluster_partition_handling, {pause_if_all_down,  ['rabbit@node1'], ignore}}
                ]
        }
 ].

 [
        {
                rabbit, [
                        {cluster_partition_handling, {pause_if_all_down,  ['rabbit@node1'], autoheal}}
                ]
        }
 ].

為什么這里會有ignore和autoheal兩種不同的配置,考慮這樣一種情況:有兩個節(jié)點(diǎn)node1和node2在機(jī)架A上,node3和node4在機(jī)架B上,此時機(jī)架A和機(jī)架B的通信出現(xiàn)異常,如果此時使用pause-minority的話會關(guān)閉所有的節(jié)點(diǎn),如果此時采用pause-if-all-down,list中配置成[‘node1’, ‘node3’]的話,集群中的4個節(jié)點(diǎn)都不會關(guān)閉,但是會形成兩個分區(qū),此時就需要ignore或者autoheal來指引如何處理此種分區(qū)的情形。

4. autoheal

在autoheal模式下,當(dāng)認(rèn)為發(fā)生網(wǎng)絡(luò)分區(qū)時,RabbitMQ會自動決定一個獲勝的(winning)分區(qū),然后重啟不在這個分區(qū)中的節(jié)點(diǎn)以恢復(fù)網(wǎng)絡(luò)分區(qū)。一個獲勝的分區(qū)是指客戶端連接最多的一個分區(qū)。如果產(chǎn)生一個平局,既有兩個或者多個分區(qū)的客戶端連接數(shù)一樣多,那么節(jié)點(diǎn)數(shù)最多的一個分區(qū)就是獲勝的分區(qū)。如果此時節(jié)點(diǎn)數(shù)也一樣多,將會以一種特殊的方式來挑選獲勝分區(qū)。
配置示例如下:

 [
        {
                rabbit, [
                        {cluster_partition_handling, autoheal}
                ]
        }
 ].
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容