docker run -d --hostname my1 --name dome-rabbit -p 15673:5672 -p 15674:15672 -e RABBITMQ_ERLANG_COOKIE='YZSDHWMFSMKEMBDHSGGZ' --dns $(docker inspect -f '{{.NetworkSettings.IPAddress}}' dns) --dns-search rabbit.com dockerhub.chinaso365.com:5043/rabbitmq:3.6.1-managementhtml
docker run -d -h my --name some-rabbit -p 5672:5672 -p 15672:15672 -e RABBITMQ_ERLANG_COOKIE='YZSDHWMFSMKEMBDHSGGZ' --dns $(docker inspect -f '{{.NetworkSettings.IPAddress}}' dns) --dns-search rabbit.com dockerhub.chinaso365.com:5043/rabbitmq:3.6.1-management前端
cat << EOF > /etc/hosts
172.17.0.13 my
172.17.0.14 my1
# test data
EOFnode
Rabbitmq集羣高可用git
RabbitMQ是用erlang開發的,集羣很是方便,由於erlang天生就是一門分佈式語言,但其自己並不支持負載均衡。github
Rabbit模式大概分爲如下三種:單一模式、普通模式、鏡像模式正則表達式
單一模式:最簡單的狀況,非集羣模式。redis
沒什麼好說的。docker
普通模式:默認的集羣模式。安全
對於Queue來講,消息實體只存在於其中一個節點,A、B兩個節點僅有相同的元數據,即隊列結構。bash
當消息進入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相對較慢,所以僅做數據備份使用,另一臺做爲反向代理。
這種模式更適合非持久化隊列,只有該隊列是非持久的,客戶端才能從新鏈接到集羣裏的其餘節點,並從新建立隊列。假如該隊列是持久化的,那麼惟一辦法是將故障節點恢復起來。
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. |
直接從官方鏡像建立後,加入集羣提示的錯誤(Error: unable to connect to nodes [rabbitmq1@rabbitmq1]...),節點沒法直接與另外一節點直接通信,增長--link也失敗。假如增長--link可行,那必須將全部集羣中的節點都使用--link進行連接,這樣就存在一個問題,後來新增的節點怎麼辦?
後來想到直接修改hosts文件,不過這種方式也比較麻煩,須要逐個修改。
最後本人在網絡中找到一個使用DNS的方式,這種方式應該更加合理。本人也作了一些簡單修改,從最新鏡像建立容器。
使用方法:git clone項目後,只需運行其中的launch.sh腳本,便可一鍵建立集羣,默認3個節點。也可閱讀後面的步驟,逐步手工建立。
原做者jrlangford的 Shell腳本
本人修改後的 Shell腳本
docker run --name dns \ -v /var/run/docker.sock:/docker.sock \ --restart='always' \ -d \ phensley/docker-dns --domain rabbit.com
docker run -d \ --name=rabbitmq1 \ -p 5672:5672 \ -p 15672:15672 \ -e RABBITMQ_NODENAME=rabbitmq1 \ -e RABBITMQ_ERLANG_COOKIE='YZSDHWMFSMKEMBDHSGGZ' \ -h rabbitmq1.rabbit.com \ --dns $(docker inspect -f '{{.NetworkSettings.IPAddress}}' dns) \ --dns-search rabbit.com \ rabbitmq:3.5-management
#磁盤節點 docker exec rabbitmq2 bash -c \ "rabbitmqctl stop_app && \ rabbitmqctl reset && \ rabbitmqctl join_cluster rabbitmq1@rabbitmq1 && \ rabbitmqctl start_app" 內存節點 docker exec rabbitmq3 bash -c \ "rabbitmqctl stop_app && \ rabbitmqctl reset && \ rabbitmqctl join_cluster --ram rabbitmq1@rabbitmq1 && \ rabbitmqctl start_app"
設置全部隊列爲鏡像隊列,除了amq開頭的交換器綁定隊列。 生成環境不建議這種處理, 應該設置部分隊列爲鏡像隊列。
docker exec rabbitmq1 rabbitmqctl set_policy HA '^(?!amq\.).*' '{"ha-mode": "all"}'
docker exec rabbitmq3 bash -c \ "rabbitmqctl stop_app && \ rabbitmqctl reset && \ rabbitmqctl start_app"
集羣節點的關閉順序與節點的啓動順序相反,即最後關閉的最早啓動,而且最後關閉的節點必須是磁盤節點。請務必遵照按此步驟處理,不然可能形成集羣沒法啓動,形成元數據,消息丟失的狀況。
Docker容器不一樣於虛擬機或主機,容器內部署的應用沒法啓動,基本上這個容器就廢了。Docker如今不容許作一些已建立容器的啓動參數調整。
1. 保證集羣中至少有一個在線,那怕這個節點是內存節點.2. 若是關閉整個集羣節點,必須按照順序逐個關閉,而且最後一個關閉節點務必爲磁盤節點。在啓動時倒着順序啓動。(如關閉順序r1,r2,r3.那麼啓動順序爲r3,r2,r1).3. 集羣中若是使用了虛擬技術(Docker,VM),虛擬節點不能全在同一臺物理機中。(你不能保證物理機不會宕機,若是宕機基本上集羣是啓動不了的.你不知道關閉順序,你就沒法肯定啓動順序,而且宕機關閉時間可能RabbitMQ節點都沒有反應過來,沒法作一些集羣狀態處理。)