前面講過一些RabbitMQ的安裝和用法,也說了說RabbitMQ在通常的業務場景下如何使用。不知道的能夠看我前面的博客,http://www.cnblogs.com/zhangweizhong/category/855479.html
html
原本一直想寫一個介紹RabbitMQ高可用的集羣的文章。不過,後來發現園子裏,有個已經RabbitMQ大牛寫了,關於高可用集羣的文章了。特別巧合的是,仍是之前公司的同事。因此,這裏就不囉嗦。直接引用過來吧。原文地址:http://www.cnblogs.com/flat_peach/archive/2013/04/07/3004008.html前端
RabbitMQ是用erlang開發的,集羣很是方便,由於erlang天生就是一門分佈式語言,但其自己並不支持負載均衡。node
Rabbit模式大概分爲如下三種:單一模式、普通模式、鏡像模式正則表達式
單一模式:最簡單的狀況,非集羣模式。redis
沒什麼好說的。安全
普通模式:默認的集羣模式。服務器
對於Queue來講,消息實體只存在於其中一個節點,A、B兩個節點僅有相同的元數據,即隊列結構。cookie
當消息進入A節點的Queue中後,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行消息傳輸,把A中的消息實體取出並通過B發送給consumer。網絡
因此consumer應儘可能鏈接每個節點,從中取消息。即對於同一個邏輯隊列,要在多個節點創建物理Queue。不然不管consumer連A或B,出口總在A,會產生瓶頸。架構
該模式存在一個問題就是當A節點故障後,B節點沒法取到A節點中還未消費的消息實體。
若是作了消息持久化,那麼得等A節點恢復,而後纔可被消費;若是沒有持久化的話,而後就沒有而後了……
鏡像模式:把須要的隊列作成鏡像隊列,存在於多個節點,屬於RabbitMQ的HA方案。
該模式解決了上述問題,其實質和普通模式不一樣之處在於,消息實體會主動在鏡像節點間同步,而不是在consumer取數據時臨時拉取。
該模式帶來的反作用也很明顯,除了下降系統性能外,若是鏡像隊列數量過多,加之大量的消息進入,集羣內部的網絡帶寬將會被這種同步通信大大消耗掉。
因此在對可靠性要求較高的場合中適用(後面會詳細介紹這種模式,目前咱們搭建的環境屬於該模式)
瞭解集羣中的基本概念:
RabbitMQ的集羣節點包括內存節點、磁盤節點。顧名思義內存節點就是將全部數據放在內存,磁盤節點將數據放在磁盤。不過,如前文所述,若是在投遞消息時,打開了消息的持久化,那麼即便是內存節點,數據仍是安全的放在磁盤。
一個rabbitmq集 羣中能夠共享 user,vhost,queue,exchange等,全部的數據和狀態都是必須在全部節點上覆制的,一個例外是,那些當前只屬於建立它的節點的消息隊列,儘管它們可見且可被全部節點讀取。rabbitmq節點能夠動態的加入到集羣中,一個節點它能夠加入到集羣中,也能夠從集羣環集羣會進行一個基本的負載均衡。
集羣中有兩種節點:
1 內存節點:只保存狀態到內存(一個例外的狀況是:持久的queue的持久內容將被保存到disk)
2 磁盤節點:保存狀態到內存和磁盤。
內存節點雖然不寫入磁盤,可是它執行比磁盤節點要好。集羣中,只須要一個磁盤節點來保存狀態 就足夠了
若是集羣中只有內存節點,那麼不能中止它們,不然全部的狀態,消息等都會丟失。
思路:
那麼具體如何實現RabbitMQ高可用,咱們先搭建一個普通集羣模式,在這個模式基礎上再配置鏡像模式實現高可用,Rabbit集羣前增長一個反向代理,生產者、消費者經過反向代理訪問RabbitMQ集羣。
架構圖以下:圖片來自http://www.nsbeta.info
集羣模式配置
設計架構能夠以下:在一個集羣裏,有4臺機器,其中1臺使用磁盤模式,另2臺使用內存模式。2臺內存模式的節點,無疑速度更快,所以客戶端(consumer、producer)鏈接訪問它們。而磁盤模式的節點,因爲磁盤IO相對較慢,所以僅做數據備份使用,另一臺做爲反向代理。
四臺服務器hostname分別爲:queue 、panyuntao一、panyuntao二、panyuntao3(ip:172.16.3.110)
配置RabbitMQ集羣很是簡單,只須要幾個命令,配置步驟以下:
step1:queue、panyuntao一、panyuntao2作爲RabbitMQ集羣節點,分別安裝RabbitMq-Server ,安裝後分別啓動RabbitMq-server
啓動命令 # Rabbit-Server start ,安裝過程及啓動命令參見:http://www.cnblogs.com/flat_peach/archive/2013/03/04/2943574.html
step2:在安裝好的三臺節點服務器中,分別修改/etc/hosts文件,指定queue、panyuntao一、panyuntao2的hosts,如:
172.16.3.32 queue
172.16.3.107 panyuntao1
172.16.3.108 panyuntao2
還有hostname文件也要正確,分別是queue、panyuntao一、panyuntao2,若是修改hostname建議安裝rabbitmq前修改。
請注意RabbitMQ集羣節點必須在同一個網段裏,若是是跨廣域網效果就差。
step3:設置每一個節點Cookie
Rabbitmq的集羣是依賴於erlang的集羣來工做的,因此必須先構建起erlang的集羣環境。Erlang的集羣中各節點是經過一個magic cookie來實現的,這個cookie存放在 /var/lib/rabbitmq/.erlang.cookie 中,文件是400的權限。因此必須保證各節點cookie保持一致,不然節點之間就沒法通訊。-r--------. 1 rabbitmq rabbitmq 20 3月 5 00:00 /var/lib/rabbitmq/.erlang.cookie將其中一臺節點上的.erlang.cookie值複製下來保存到其餘節點上。或者使用scp的方法也可,可是要注意文件的權限和屬主屬組。咱們這裏將queue中的cookie 複製到 panyuntao一、panyuntao2中,先修改下panyuntao一、panyuntao2中的.erlang.cookie權限#chmod 777 /var/lib/rabbitmq/.erlang.cookie將queue的/var/lib/rabbitmq/.erlang.cookie這個文件,拷貝到panyuntao一、panyuntao2的同一位置(反過來亦可),該文件是集羣節點進行通訊的驗證密鑰,全部節點必須一致。拷完後重啓下RabbitMQ。複製好後別忘記還原.erlang.cookie的權限,不然可能會遇到錯誤#chmod 400 /var/lib/rabbitmq/.erlang.cookie設置好cookie後先將三個節點的rabbitmq重啓# rabbitmqctl stop# rabbitmq-server start
[{nodes,[{disc,[rabbit@queue]}]},
{running_nodes,[rabbit@queue]},
{partitions,[]}]
...done.
[{nodes,[{disc,[rabbit@panyuntao1]}]},
{running_nodes,[rabbit@panyuntao1]},
{partitions,[]}]
...done.
[{nodes,[{disc,[rabbit@panyuntao2]}]},
{running_nodes,[rabbit@panyuntao2]},
{partitions,[]}]
...done.
panyuntao1# rabbitmqctl join_cluster --ram rabbit@queue
panyuntao1# rabbitmqctl start_app
panyuntao2# rabbitmqctl stop_apppanyuntao2# rabbitmqctl join_cluster --ram rabbit@queue (上方已經將panyuntao1與queue鏈接,也能夠直接將panyuntao2與panyuntao1鏈接,一樣而已加入集羣中)panyuntao2# rabbitmqctl start_app
只要在節點列表裏包含了本身,它就成爲一個磁盤節點。在RabbitMQ集羣裏,必須至少有一個磁盤節點存在。
step5:在queue、panyuntao一、panyuntao2上,運行cluster_status命令查看集羣狀態:
[root@queue ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@queue ...
[{nodes,[{disc,[rabbit@queue]},{ram,[rabbit@panyuntao2,rabbit@panyuntao1]}]},
{running_nodes,[rabbit@panyuntao2,rabbit@panyuntao1,rabbit@queue]},
{partitions,[]}]
...done.
[root@panyuntao1 rabbitmq]# rabbitmqctl cluster_status
Cluster status of node rabbit@panyuntao1 ...
[{nodes,[{disc,[rabbit@queue]},{ram,[rabbit@panyuntao2,rabbit@panyuntao1]}]},
{running_nodes,[rabbit@panyuntao2,rabbit@queue,rabbit@panyuntao1]},
{partitions,[]}]
...done.[root@panyuntao2 rabbitmq]# rabbitmqctl cluster_status
Cluster status of node rabbit@panyuntao2 ...
[{nodes,[{disc,[rabbit@queue]},{ram,[rabbit@panyuntao2,rabbit@panyuntao1]}]},
{running_nodes,[rabbit@panyuntao1,rabbit@queue,rabbit@panyuntao2]},
{partitions,[]}]
...done.這時咱們能夠看到每一個節點的集羣信息,分別有兩個內存節點一個磁盤節點
root@panyuntao2 :~# rabbitmqctl list_queues -p hrsystem
Listing queues …
test_queue 10000
…done.root@panyuntao1 :~# rabbitmqctl list_queues -p hrsystemListing queues …
test_queue 10000
…done.root@queue:~# rabbitmqctl list_queues -p hrsystemListing queues …
test_queue 10000
…done.-p參數爲vhost名稱
1. storage space: If every cluster node had a full copy of every queue, adding nodes wouldn’t give you more storage capacity. For example, if one node could store 1GB of messages, adding two more nodes would simply give you two more copies of the same 1GB of messages.
2. performance: Publishing messages would require replicating those messages to every cluster node. For durable messages that would require triggering disk activity on all nodes for every message. Your network and disk load would increase every time you added a node, keeping the performance of the cluster the same (or possibly worse).
固然RabbitMQ新版本集羣也支持隊列複製(有個選項能夠配置)。好比在有五個節點的集羣裏,能夠指定某個隊列的內容在2個節點上進行存儲,從而在性能與高可用性之間取得一個平衡。
上面配置RabbitMQ默認集羣模式,但並不保證隊列的高可用性,儘管交換機、綁定這些能夠複製到集羣裏的任何一個節點,可是隊列內容不會複製,雖然該模式解決一部分節點壓力,但隊列節點宕機直接致使該隊列沒法使用,只能等待重啓,因此要想在隊列節點宕機或故障也能正常使用,就要複製隊列內容到集羣裏的每一個節點,須要建立鏡像隊列。咱們看看如何鏡像模式來解決複製的問題,從而提升可用性
關於負載均衡器,商業的好比F5的BIG-IP,Radware的AppDirector,是硬件架構的產品,能夠實現很高的處理能力。但這些產品昂貴的價格會讓人止步,因此咱們還有軟件負載均衡方案。互聯網公司經常使用的軟件LB通常有LVS、HAProxy、Nginx等。LVS是一個內核層的產品,主要在第四層負責數據包轉發,使用較複雜。HAProxy和Nginx是應用層的產品,但Nginx主要用於處理HTTP,因此這裏選擇HAProxy做爲RabbitMQ前端的LB。
HAProxy的安裝使用很是簡單,在Centos下直接yum install haproxy,而後更改/etc/haproxy/haproxy.cfg 文件便可,文件內容大概以下:
#---------------------------------------------------------------------defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000listen rabbitmq_cluster 0.0.0.0:5672mode tcpbalance roundrobinserver rqslave1 172.16.3.107:5672 check inter 2000 rise 2 fall 3server rqslave2 172.16.3.108:5672 check inter 2000 rise 2 fall 3# server rqmaster 172.16.3.32:5672 check inter 2000 rise 2 fall 3#---------------------------------------------------------------------
在cluster中任意節點啓用策略,策略會自動同步到集羣節點
rabbitmqctl set_policy -p hrsystem ha-allqueue"^" '{"ha-mode":"all"}'#
這行命令在vhost名稱爲hrsystem建立了一個策略,策略名稱爲ha-allqueue,策略模式爲 all 即複製到全部節點,包含新增節點,
策略正則表達式爲 「^」 表示全部匹配全部隊列名稱。
例如rabbitmqctl set_policy -p hrsystem ha-allqueue "^message" '{"ha-mode":"all"}'
注意:"
^message" 這個規則要根據本身修改,這個是指同步"message"開頭的隊列名稱,咱們配置時使用的應用於全部隊列,因此表達式爲"^"
官方set_policy說明參見
ha-mode | ha-params | Result |
---|---|---|
all | (absent) | Queue is mirrored across all nodes in the cluster. When a new node is added to the cluster, the queue will be mirrored to that node. |
exactly | count | Queue is mirrored to count nodes in the cluster. If there are less than count nodes in the cluster, the queue is mirrored to all nodes. If there are more than countnodes in the cluster, and a node containing a mirror goes down, then a new mirror will not be created on another node. (This is to prevent queues migrating across a cluster as it is brought down.) |
nodes | node names | Queue is mirrored to the nodes listed in node names. If any of those node names are not a part of the cluster, this does not constitute an error. If none of the nodes in the list are online at the time when the queue is declared then the queue will be created on the node that the declaring client is connected to. |
參考:
http://www.rabbitmq.com/clustering.html
http://www.rabbitmq.com/ha.html
http://www.rabbitmq.com/parameters.html#policies