理解 OpenStack 高可用(HA)(5):RabbitMQ HA


本系列會分析OpenStack 的高可用性(HA)概念和解決方案:

(1)OpenStack 高可用方案概述

(2)Neutron L3 Agent HA - VRRP (虛擬路由冗余協議)

(3)Neutron L3 Agent HA - DVR (分布式虛機路由器)

(4)Pacemaker 和 OpenStack Resource Agent (RA)

(5)RabbitMQ HA

(6)MySQL HA

1. RabbitMQ 集群

    你可以使用若干個RabbitMQ 節點組成一個 RabbitMQ 集群。集群解決的是擴展性問題。所有的數據和狀態都會在集群內所有的節點上被復制,只有queue是例外。默認的情況下,消息只會存在於它被創建的節點上,但是它們在所有節點上可見和可訪問。

    對於Queue 來說,消息實體只存在於其中一個節點,A、B兩個節點僅有相同的元數據,即隊列結構。當消息進入A節點的Queue中后,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行消息傳輸,把A中的消息實體取出並經過B發送給consumer。所以consumer應盡量連接每一個節點,從中取消息。即對於同一個邏輯隊列,要在多個節點建立物理Queue。否則無論consumer連A或B,出口總在A,會產生瓶頸。該模式存在一個問題就是當A節點故障后,B節點無法取到A節點中還未消費的消息實體。如果做了消息持久化,那么得等A節點恢復,然后才可被消費;如果沒有持久化的話,然后就沒有然后了。

   因此,在集群環境中,隊列只有元數據會在集群的所有節點同步,但隊列中的數據只會存在於一個節點,數據沒有冗余且容易丟,甚至在durable的情況下,如果所在的服務器節點宕機,就要等待節點恢復才能繼續提供消息服務。那既然有這種問題,為什么依然有這個選項呢?官方的說法是:

   (1)存儲空間:如果集群的每個節點都有每個queue的一個拷貝,那么增加節點將無法增加存儲容量。比如,如果一個節點可以存放 1GB 的消息,增加另外兩個節點只會增加另外兩個拷貝而已。

  (2)性能:發布消息,將會將它拷貝其它節點上。如果是持久性消息,那么每個節點上都會觸發磁盤操作。你的網絡和磁盤負載在每次增加節點時都會增加。

  可見,RabbitMQ Clustering 技術不能完全解決HA 問題。單純的集群只適合於在不需要HA的場景中使用。

2. Active/Passive HA 方案

    RabbitMQ A/P HA 官方方案 是采用 Pacemaker + (DRBD 或者其它可靠的共享 NAS/SNA 存儲) + (CoroSync 或者 Heartbeat 或者 OpenAIS)組合來實現的。需要注意的是 CoroSync 需要使用多播,而多播在某些雲環境中是被禁用的,這時候可以改為使用 Heartbeat,它采用單播。其架構為:

實現 HA 的原理:

  1. RabbitMQ 將數據庫文件和持久性消息保存在 DRBD 掛載點上。注意,在 failover 時,非持久性消息會丟失。
  2. DRBD 要求在某一時刻,只能有一個 node (primary)能夠訪問其共享存儲,因此,不會有多個node 同時寫一塊數據的風險。這要求必須依賴 Pacemaker 來控制使用 DRBD 共享存儲的 node。Pacemaker 使得在某一時刻,只有 active node 來訪問 DRBD 共享存儲。在 failover 時,Pacemaker 會卸載當前 active node 上的 DRBD 共享存儲,並在新的 active node (原 secondary node)上掛載 DRBD 共享存儲。
  3. 在 node 啟動時,不會自動啟動 RabbitMQ。Pacemaker 會在 active node 啟動 RabbitMQ。
  4. RabbitMQ HA 只會提供一個訪問 RabbitMQ 的 虛擬IP 地址。該方案依賴 Pacemaker 來保證 VIP 的切換。

2.1 基於 Pacemaker + DRBD + CoroSync 的 RabbitMQ HA 方案配置

    RabbitMQ 官方的 這篇文章介紹了基於 Pacemaker 的 RabbitMQ HA 方案。它同時指出,這是傳統的 RabbitMQ HA 方案,並且建議使用 RabbitMQ 集群 + 鏡像 Message Queue 的方式來實現 A/A HA。使用 Pacemaker 實現 HA 的方案主要步驟包括:

  1. 為 RabbitMQ 配置一個 DRBD 設備

  2. 配置 RabbitMQ 使用建立在 DRBD 設備之上的數據目錄

  3. 選擇並綁定一個可以在各集群節點之間遷移的虛擬 IP 地址 (即 VIP )

  4. 配置 RabbitMQ 監聽該 IP 地址

  5. 使用 Pacemaker 管理上述所有資源,包括 RabbitMQ 守護進程本身

2.1.1 安裝 Corosync 和 Pacemaker

本配置使用兩個節點。

首先,在兩個節點上安裝軟件包:apt-get install pacemaker crmsh corosync cluster-glue resource-agents,並配置 Corosync,設它為 boot 自啟動:vi /etc/default/corosync,START=yes。

兩個節點上,

(1)修改配置文件:vim /etc/corosync/corosync.conf

totem {
        #...
        interface {
                ringnumber: 0
                bindnetaddr: #節點1上,192.168.1.15;節點2上,192.168.1.32
                broadcast: yes
                mcastport: 5405
        }
        transport: udpu
}
 
nodelist {
        node {
                ring0_addr: 192.168.1.15
                nodeid: 1
        }
        node {
                ring0_addr: 192.168.1.32
                nodeid: 2
        }
}

(2)啟動服務 service corosync start

(3)查看狀態

root@compute2:/home/s1# corosync-cfgtool -s

Printing ring status.
Local node ID 2
RING ID 0
id = 192.168.1.32
status = ring 0 active with no faults

root@compute2:/home/s1# corosync-cmapctl runtime.totem.pg.mrp.srp.members #需要確保兩個節點都加入了組
runtime.totem.pg.mrp.srp.members.1.config_version (u64) = 0
runtime.totem.pg.mrp.srp.members.1.ip (str) = r(0) ip(192.168.1.15)
runtime.totem.pg.mrp.srp.members.1.join_count (u32) = 1
runtime.totem.pg.mrp.srp.members.1.status (str) = joined
runtime.totem.pg.mrp.srp.members.2.config_version (u64) = 0
runtime.totem.pg.mrp.srp.members.2.ip (str) = r(0) ip(192.168.1.32)
runtime.totem.pg.mrp.srp.members.2.join_count (u32) = 1
runtime.totem.pg.mrp.srp.members.2.status (str) = joined

(4)啟動 pacemaker:service pacemaker start

(5)查看 pacemaker 狀態:crm_mon

Last updated: Sun Aug 16 15:59:10 2015
Last change: Sun Aug 16 15:58:59 2015 via crmd on compute1
Stack: corosync
Current DC: compute1 (1) - partition with quorum
Version: 1.1.10-42f2063
3 Nodes configured
0 Resources configured

Node compute1 (1084752143): UNCLEAN (offline)
Online: [ compute1 compute2 ]

(6)配置 pacemaker

root@compute2:/home/s1# crm
crm(live)# configure
crm(live)configure# property no-quorum-policy="ignore" \ #因為這里只有兩個節點,因此需要設置為 ‘ignore’。兩個節點以上,不可以設置為 ‘ignore’ >   pe-warn-series-max="1000" \
>   pe-input-series-max="1000" \
>   pe-error-series-max="1000" \
>   cluster-recheck-interval="2min"
crm(live)configure# quit
There are changes pending. Do you want to commit them? yes
bye

2.1.2 為 RabbitMQ 配置一個 DRBD 設備

 在 node1 和 node2 上,依次進行:

(0)安裝 DRBD,並設置機器啟動時 DRBD 服務不自動啟動。該服務的啟停升降級等都需要由 Pacemaker 控制。

(1)添加一個 1G 的 hard disk,fdiskl -l 可見 /dev/sdb

(2)創建 LVM:pvcreate /dev/sdb,vgcreate vg_rabbit /dev/sdb,lvcreate -L1000 -n rabbit vg_rabbit。創建好的 LVM 為 /dev/mapper/vg_rabbit-rabbit。

(3)定義 rabbitmq 資源:vi /etc/drbd.d/rabbitmq.res

根據 /etc/drbd.conf,資源的配置文件需要放置在 /etc/drbd.d/ 目錄中,並以 res 為文件類型。DRBD 會自動讀取這些配置文件並創建相應的資源。

rource rabbitmq {
  device    minor 1;               # DRBD device 是 /dev/drbd1,相當於 device /dev/drbd1
  disk      "/dev/mapper/vg_rabbit-rabbit"; # Raw device, 本例中是 LVM path。也可以使用不同名字的 Raw device,這樣的話需要放到 host 定義部分。
  meta-disk internal;               # 這是默認值,更多信息可以參考這個
  on compute1 {                     # 節點1,名稱需要與 uname -n 的輸出相同。
    address ipv4 192.168.1.15:7701; #7701 是 DRBD 通信的 TCP 端口,需要確保該端口可以被訪問
  }
  on compute2 {                     # 節點2
    address ipv4 192.168.1.32:7701;
  }
}

 (4)drbdadm create-md rabbitmq

root@compute2:/home/s1# drbdadm create-md rabbitmq
Writing meta data...
initializing activity log
NOT initializing bitmap
New drbd meta data block successfully created.
(5)drbdadm up rabbitmq

在 compute 2 上,
(1)drbdadm -- --force primary rabbitmq
(2)mkfs -t xfs /dev/drbd1 (如果提示 mkfs.xsf 找不到,則 sudo apt-get install xfsprogs)
meta-data=/dev/drbd1             isize=256    agcount=4, agsize=63996 blks
         =                       sectsz=512   attr=2, projid32bit=0
data     =                       bsize=4096   blocks=255983, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0
log      =internal log           bsize=4096   blocks=1200, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

(3)mount /dev/drbd1 /var/lib/rabbitmq

(4)umount /var/lib/rabbitmq

(3)drbdadm secondary rabbitmq

在 compute 1 上:

  1. drbdadm -- --force primary rabbitmq
  2. mkfs -t xfs /dev/drbd1
  3. mount /dev/drbd1 /var/lib/rabbitmq

至此,DRBD 設置完成,目前 compute 1 上的 DRBD 是主,compute 2 上的是備。查看其狀態:

root@compute1:/home/s1# /etc/init.d/drbd status
drbd driver loaded OK; device status:
version: 8.4.3 (api:1/proto:86-101)
srcversion: 6551AD2C98F533733BE558C
m:res       cs         ro                 ds                 p  mounted            fstype
1:rabbitmq  Connected  Primary/Secondary  UpToDate/UpToDate  C  /var/lib/rabbitmq  xfs

root@compute2:/home/s1# /etc/init.d/drbd status
drbd driver loaded OK; device status:
version: 8.4.3 (api:1/proto:86-101)
srcversion: 6551AD2C98F533733BE558C
m:res       cs         ro                 ds                 p  mounted  fstype
1:rabbitmq  Connected  Secondary/Primary  UpToDate/UpToDate  C

調試時遇到一個腦裂問題,就是兩個node上 drbd 的狀態都是 StandAlone,然后 DRBD 服務在 compute 2 上能起來,在 compute 1 上起不來。

root@compute1:/home/s1# /etc/init.d/drbd status
drbd driver loaded OK; device status:
version: 8.4.3 (api:1/proto:86-101)
srcversion: 6551AD2C98F533733BE558C
m:res       cs          ro                 ds                 p       mounted  fstype
1:rabbitmq  StandAlone  Secondary/Unknown  UpToDate/DUnknown  r-----

DRBD's startup script waits for the peer node(s) to appear.
 - In case this node was already a degraded cluster before the
   reboot the timeout is 0 seconds. [degr-wfc-timeout]
 - If the peer was available before the reboot the timeout will
   expire after 0 seconds. [wfc-timeout]
   (These values are for resource 'rabbitmq'; 0 sec -> wait forever)
 To abort waiting enter 'yes' [ 148]:

這篇文章 和 這篇文章 給出了這個問題的解決辦法:

  • 在起不來 DRBD 服務的節點(compute1)上,運行 drbdadm disconnect rabbitmq,drbdadm secondary rabbitmq,和 drbdadm --discard-my-data connect rabbitmq
  • 在能起來 DRBD 服務的節點(compute2)上,運行 drbdadm connect rabbitmq
  • 然后 DRBD 狀態恢復成 Connected 了。

SUSE 有關於 DRBD 安裝、配置和測試的 不錯的文檔 。

2.1.3 安裝和配置 RabbitMQ

(1)在兩個節點上安裝 RabbitMQ。rabbitmq-server 進程會在 rabbitmq 組內的 rabbitmq 用戶下運行。

(2)確保 rabbitmq 組和用戶在兩個節點上具有相同的 ID。運行 cat /etc/passwd | grep rabbitmq 和 cat /etc/group | grep rabbitmq 查看 ID,必要時進行修改。

(3)確保 rabbitmq 用戶對 /var/lib/rabbitmq/ 有讀寫權限。 運行 chmod a+w /var/lib/rabbitmq/。

(4)確保各節點都使用相同的 Erlang cookie。需要將 compute 1 上的 RabbitMQ cookie 拷貝到 compute 2 上:

(1)compute 1:scp /var/lib/rabbitmq/.erlang.cookie s1@compute2:/home/s1
(2)compute 2:mv .erlang.cookie /var/lib/rabbitmq/
(3)
compute 2:chown rabbitmq: /var/lib/rabbitmq/.erlang.cookie
(4)compute 2:chmod 400 /var/lib/rabbitmq/.erlang.cookie
(5)再執行相同的步驟,將該文件拷貝到 DRBD 共享存儲文件系統中

(5)確保計算機啟動時不自動啟動 rabbitmq。修改 /etc/init.d/rabbitmq-server 文件,在文件頭注釋后面添加 exit 0.

2.1.4 在 Pacemaker 中添加 RabbitMQ 資源

在添加前,在 compute 1 節點上,運行 crm configure,輸入:

primitive p_ip_rabbitmq ocf:heartbeat:IPaddr2 \  #定義訪問 RabbitMQ 的 VIP。使用 RA ocf:heartbeat:IPaddr2。對該 VIP 的監控間隔為 10s。 params ip="192.168.1.222" cidr_netmask="24" \
  op monitor interval="10s"
primitive p_drbd_rabbitmq ocf:linbit:drbd \ #定義RabbitMQ 的 DRBD 資源,pacemaker 會對它 start,stop,promote,demote 和 monitor。 params drbd_resource="rabbitmq" \
  op start timeout="90s" \ #啟動的超時時間
  op stop timeout="180s" \
  op promote timeout="180s" \
  op demote timeout="180s" \
  op monitor interval="30s" role="Slave" \ #對 Slave drbd 的監控間隔定義為 30s
  op monitor interval="29s" role="Master"  #對 master drbd 的監控間隔定義為 29s
primitive p_fs_rabbitmq ocf:heartbeat:Filesystem \ # 在 /dev/drbd1 上創建 xfs 文件系統,並 mount 到 /var/lib/rabbitmq params device="/dev/drbd1" directory="/var/lib/rabbitmq" fstype="xfs" options="relatime" \
  op start timeout="60s" op stop timeout="180s" op monitor interval="60s" timeout="60s"
primitive p_rabbitmq ocf:rabbitmq:rabbitmq-server \ #定義 RabbitMQ 資源 params nodename="rabbit@localhost" \
    mnesia_base="/var/lib/rabbitmq" \
  op monitor interval="20s" timeout="10s" group g_rabbitmq p_ip_rabbitmq p_fs_rabbitmq p_rabbitmq #group 指定一組資源需要在同一個node上 ip -> fs -> rabbitmq,順序啟動,逆序停止
ms ms_drbd_rabbitmq p_drbd_rabbitmq meta notify="true" master-max="1" clone-max="2" # 定義一個資源集合,往往用於 master-slave 資源集群。其中,clone-max 定義集合內的 drbd 節點數目,master-max 指定 master 最多只能有一個;notify = “true” 表示啟用notification。 colocation c_rabbitmq_on_drbd inf: g_rabbitmq ms_drbd_rabbitmq:Master #colocation 定義在同一個節點上啟動的資源。這里定義了一個約束,rabbitmq 只能在 DRBD Master 節點上啟動
order o_drbd_before_rabbitmq inf: ms_drbd_rabbitmq:promote g_rabbitmq:start #oder 指定服務啟動順序。這個定義要求先 DRBD 升級為 Master 然后依次執行 ip,fs 和 rabbitmq 啟動。

更多的 Pacemaker 配置細節,請參考 這個文檔

Pacemaker 一些有用的命令:

  • crm status:顯示集群狀態
  • crm configure show:顯示集群配置
  • crm configure edit:修改集群配置
  • crm node standby:將當前節點設為備節點
  • crm node online:將當前節點設為主節點
  • crm ra meta ocf:heartbeat:IPaddr2:查看 RA 的 metadata 

配置完成后,RabbitMQ 起不來。一堆問題,挨個來說:

(1)Corosync 調試:設置日志文件

logging {
        fileline: off
        to_stderr: yes
        to_logfile: yes
        to_syslog: yes
        logfile: /var/log/cluster/corosync.log
        syslog_facility: daemon
        debug: off
        timestamp: on        
        }

(2)pacemaker 調試:pacemaker 的日志在 corosync 的 log file 中。

(3)運行 crm status,如果出現  Could not establish cib_ro connection: Connection refused (111) ,原因是 pacemaker 服務沒有啟動。運行 service pacemaker start 即可。

(4)rabbitmq 啟動的錯誤日志可以在 /var/log/rabbitmq 目錄下找到。可能的錯誤之一是,rabbit 用戶沒有目錄 /var/lib/rabbitmq 的寫權限。

成功后,是這樣的效果:

root@compute2:/var/lib/rabbitmq/rabbit@localhost# crm status
Last updated: Mon Aug 17 00:16:11 2015
Last change: Sun Aug 16 22:54:27 2015 via cibadmin on compute1
Stack: corosync
Current DC: compute1 (1) - partition with quorum
Version: 1.1.10-42f2063
3 Nodes configured
5 Resources configured

Online: [ compute1 compute2 ] # pacemaker 集群中有兩個節點
OFFLINE: [ compute1 ]         # compute 1 是備節點

 Resource Group: g_rabbitmq
     p_ip_rabbitmq      (ocf::heartbeat:IPaddr2):       Started compute2 #在 compute 2 上啟動 VIP,成功
     p_rabbitmq (ocf::rabbitmq:rabbitmq-server):        Started compute2 #在 compute 2 上啟動 rabbitmq,成功
 Master/Slave Set: ms_drbd_rabbitmq [p_drbd_rabbitmq]
     Masters: [ compute2 ]
     Slaves: [ compute1 ]
 p_fs_rabbitmq  (ocf::heartbeat:Filesystem):    Started compute2 # 在 compute 2 上設置 drbd 文件系統,成功

可見,根據配置,pacemaker 會(1)根據 corosync 確定集群中的節點和狀態 (2)啟動 VIP (3)啟動指定的服務 (4)設置 DRBD 文件系統。每一步都可能失敗,失敗則需要調試。

(5)將主節點停機,rabbitmq 服務順利地被切換到備節點上。

2. 基於集群+鏡像隊列的A/A 方案 

    從 3.6.0 版本開始,RabbitMQ 支持鏡像隊列功能,官方文檔在這里。。與普通集群相比,其實質和普通模式不同之處在於,消息實體會主動在鏡像節點間同步,而不是在 consumer 取數據時臨時拉取。該模式帶來的副作用也很明顯,除了降低系統性能外,如果鏡像隊列數量過多,加之大量的消息進入,集群內部的網絡帶寬將會被這種同步通訊大大消耗掉。所以在對可靠性要求較高的場合中適用。 

    Mirrorred queue 是 RabbitMQ 高可用的一種方案,相對於普通的集群方案來講,queue中的消息每個節點都會存在一份 copy, 這個在單個節點失效的情況下,整個集群仍舊可以提供服務。但是由於數據需要在多個節點復制,在增加可用性的同時,系統的吞吐量會有所下降。 

    選舉機制:mirror queue 內部實現了一套選舉算法,有一個 master 和多個slave,queue 中的消息以 master 為主。鏡像隊列有主從之分,一個主節點(master),0個或多個從節點(slave)。當master宕掉后,會在 slave 中選舉新的master,其選舉算法為最早啟動的節點。 若master節點失效,則 mirror queue 會自動選舉出一個節點(slave中消息隊列最長者)作為master,作為消息消費的基准參考; 在這種情況下可能存在ack消息未同步到所有節點的情況(默認異步),若 slave 節點失效,mirror queue 集群中其他節點的狀態無需改變。所以,看起來可以使用兩節點的鏡像隊列集群。 

    使用:對於publish,可以選擇任意一個節點進行連接,rabbitmq內部若該節點不是master,則轉發給master,master向其他slave節點發送該消息,后進行消息本地化處理,並組播復制消息到其他節點存儲;對於consumer,可以選擇任意一個節點進行連接,消費的請求會轉發給master,為保證消息的可靠性,consumer需要進行ack確認,master收到ack后,才會刪除消息,ack消息會同步(默認異步)到其他各個節點,進行slave節點刪除消息。   

   考慮到性能問題,復制可以在可控范圍內進行,包括集群內全節點復制的鏡像隊列和集群內局部節點復制的鏡像隊列兩種模式。 

2.1 配置

    多個單獨的 RabbitMQ 服務,可以加入到一個集群中,也可以從集群中退出。集群中的 RabbitMQ 服務,使用同樣的 Erlang cookie(unix 系統上默認為 /var/lib/rabbitmq/.erlang.cookie)。所有在一個 RabbitMQ 服務中被操作的數據和狀態(date/state)都會被同步到集群中的其它節點。  

   鏡像隊列的配置通過添加 policy 完成,policy 添加的命令為:

  • rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]
  • -p Vhost: 可選參數,針對指定vhost下的queue進行設置
  • Name: policy的名稱
  • Pattern: queue的匹配模式(正則表達式)
  • Definition: 鏡像定義,包括三個部分 ha-mode,ha-params,ha-sync-mode
    • ha-mode: 指明鏡像隊列的模式,有效值為 all/exactly/nodes
      • all表示在集群所有的節點上進行鏡像
      • exactly 表示在指定個數的節點上進行鏡像,節點的個數由ha-params指定
      • nodes 表示在指定的節點上進行鏡像,節點名稱通過ha-params指定
    • ha-params: ha-mode模式需要用到的參數
    • ha-sync-mode: 鏡像隊列中消息的同步方式,有效值為automatic,manually
  • Priority: 可選參數, policy的優先級

例如,對隊列名稱以 ’hello‘ 開頭的所有隊列進行鏡像,並在集群的兩個節點上完成鏡像,policy的設置命令為:

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

2.2 HAProxy 和 RabbitMQ A/A 集群

RabbitMQ 實現鏡像隊列的方式比較特別。這篇文章進行了深入的闡述。假設有如下的配置:

創建 queue 的過程:

  1. LB 將 client request 分發到 node 2,client 創建隊列 “NewQueue”,然后開始向其中放入 message。
  2. 最終,后端服務會對 node 2 上的 “NewQueue” 創建一個快照,並在一段時間內將其拷貝到node 1 和 3 上。這時候,node2 上的隊列是 master Queue,node 1 和 3 上的隊列是 slave queue。

假如現在 node2 宕機了:

  • node 2 不再響應心跳,它會被認為已經被從集群中移出了
  • node 2 上的 master queue 不再可用
  • RabbitMQ 將 node 1 或者 3 上的 salve instance 升級為 master instance

假設 master queue 還在 node 2 上,客戶端通過 LB 訪問該隊列:

  1. 客戶端連接到集群,要訪問 “NewQueue” 隊列
  2. LB 根據配置的輪詢算法將請求分發到一個節點上
  3. 假設客戶端請求被轉到 node 3 上
  4. RabbitMQ 發現 “NewQueue” master node 是 node 2
  5. RabbitMQ 將消息轉到 node 2 上
  6. 最終客戶端成功連接到 node 2 上的 master 隊列

可見,這種配置下,2/3 的客戶端請求需要重定向,這會造成大概率的訪問延遲,但是終究訪問還是會成功的。要優化的話,總共有兩種方式:

  • 直接連到 master queue 所在的節點,這樣就不需要重定向了。但是對這種方式,需要提前計算,然后告訴客戶端哪個節點上有 master queue。
  • 盡可能地在所有節點間平均分布隊列,減少重定向概率

2.3 鏡像隊列的負載均衡

     使用鏡像隊列的 RabbitMQ 不支持負載均衡,這是由其鏡像隊列的實現機制決定的。如前面所述,假設一個集群里有兩個實例,記作 rabbitA 和 rabbitB。如果某個隊列在rabbitA 上創建,隨后在 rabbitB 上鏡像備份,那么 rabbitA 上的隊列稱為該隊列的主隊列(master queue),其它備份均為從隊列。接下來,無論client 訪問rabbitA 或 rabbitB,最終消費的隊列都是主隊列。換句話說,即使在連接時主動連接rabbitB,RabbitMQ的 cluster 會自動把連接轉向 rabbitA。當且僅當rabbitA服務down掉以后,在剩余的從隊列中再選舉一個作為繼任的主隊列。

    如果這種機制是真的(需要看代碼最最終確認),那么負載均衡就不能簡單地隨機化連接就能做到了。要實現輪詢,需要滿足下面的條件:

  • 隊列本身的建立需要隨機化,即將隊列分布於各個服務器
  • client 訪問需要知道每個隊列的主隊列保存在哪個服務器
  • 如果某個服務器down了,需要知道哪個從隊列被選擇成為繼任的主隊列。

   要實現這種方案,這篇文章 給出了一個方案:

首先,在建立一個新隊列的時候,Randomiser 會隨機選擇一個服務器,這樣能夠保證隊列均勻分散在各個服務器(這里暫且不考慮負載)。建立隊列后需要在Meta data 里記錄這個隊列對應的服務器;另外,Monitor Service是關鍵,它用於處理某個服務器down掉的情況。一旦發生down機,它需要為之前主隊列在該服務器的隊列重新建立起與服務器的映射關系。

這里會遇到一個問題,即怎么判斷某個隊列的主隊列呢?一個方法是通過rabbitmqctl,如下面的例子: 

 可以看到pid和slave_pids分別對應主隊列所在的服務器和從服務器(可能有多個)。利用這個命令就可以了解每個隊列所在的主服務器了。

3. OpenStack 中RabbitMQ集群的用法和配置

3.1 配置

    OpenStack 官方建議至少使用三節點 RabbitMQ 集群,而且推薦配置是使用鏡像隊列。對於測試和演示環境,使用兩節點也是可以。以下OpenStack 服務都支持這種 A/A 形式的 RabbitMQ:

  • 計算服務
  • 塊設備存儲服務
  • 網絡服務
  • Telemetry

    OpenStack 支持如下的 RabbitMQ 配置:

  • rabbit_hosts=rabbit1:5672,rabbit2:5672,rabbit3:5672:所有RabbitMQ 服務列表
  • rabbit_retry_interval=1: 連接失敗時候的重試間隔
  • rabbit_retry_backoff=2: How long to back-off for between retries when connecting to RabbitMQ。不太明白其含義。
  • rabbit_max_retries=0:最大重試次數。0 表示一直重試
  • rabbit_durable_queues=true:true 的話表示使用持久性隊列,Kilo 中默認為 false。
  • rabbit_ha_queues=true: 設置為 true 的話則使用鏡像隊列,並設置 x-ha-policy 為 all;但是 Kilo 中其默認值為 false。

具體的配置可以參考 這篇文章 以及 OpenStack 官網,以及 RabbitMQ 官網

3.2 RabbitMQ 節點選擇

OpenStack 的 oslo_messaging 的 RabbitMQ driver 的實現代碼在 這里。它采用的邏輯正是上面所提到的第二種優化方法“盡可能地在所有節點間平均分布隊列,減少重定向概率”。這表現在:

(1)將 rabbit_hosts 打亂,然后構造新的包含多host的 url:

if len(self.rabbit_hosts) > 1:
    random.shuffle(self.rabbit_hosts) #將多個 hosts 打亂
for adr in self.rabbit_hosts:
    hostname, port = netutils.parse_host_port(
        adr, default_port=self.rabbit_port)
    self._url += '%samqp://%s:%s@%s:%s/%s' % ( #將多個url合並,以分號分割,kombu 會按照指定策略在連接失敗時重新選擇別的host重連
        ";" if self._url else '',
        parse.quote(self.rabbit_userid, ''),
        parse.quote(self.rabbit_password, ''),
        self._parse_url_hostname(hostname), port,
        virtual_host)

(2)在建立 Connection 的時候,設置 failover_strategy 為 “shuffle”,這樣,在連接不成功需要重連的時候,kombu 會 “隨機”地選擇一個新的 host:port 重新連接(kombu 庫支持 輪詢和隨機兩種策略,代碼在這里

self.connection = kombu.connection.Connection(
    self._url, ssl=self._fetch_ssl_params(),
    login_method=self.login_method,
    failover_strategy="shuffle",
    heartbeat=self.heartbeat_timeout_threshold,
    transport_options={
        'confirm_publish': True,
        'on_blocked': self._on_connection_blocked,
        'on_unblocked': self._on_connection_unblocked,
    },
)

3.3 性能配置

OpenStack 支持如下幾種 RPC 性能配置:

  • rpc_conn_pool_size = 30 (IntOpt) (RPC 連接池的大小)
  • rpc_response_timeout = 60 (IntOpt) (等待返回的超時時間,單位是秒)
  • rpc_thread_pool_size = 64 (IntOpt) (RPC 線程池的大小)

 這些參數在做RPC 性能調試的時候往往需要考慮到。

參考鏈接:

 


免責聲明!

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



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