1. 程式人生 > >RabbitMQ+HAProxy構建高可用訊息佇列

RabbitMQ+HAProxy構建高可用訊息佇列

用RabbitMQ的叢集、映象佇列+HAProxy構建一個高可用的訊息佇列。

叢集配置

1、三臺機器都需要在/etc/hosts裡面輸入相關的IP和機器名對應關係

192.168.56.111 ubuntu01
192.168.56.112 ubuntu02
192.168.56.113 ubuntu03

2、設定同一cookie

檢視mq1的cookie

$ sudo cat /var/lib/rabbitmq/.erlang.cookie    #RFHZQHILNIHDGGKQYYNG

設定ubuntu02、ubuntu03的cookie為這個值,需要臨時修改許可權才能新增進去

$ sudo rabbitmqctl stop
$ sudo chmod 777 /var/lib/rabbitmq/.erlang.cookie
$ sudo echo 'RFHZQHILNIHDGGKQYYNG' >  /var/lib/rabbitmq/.erlang.cookie
$ sudo chmod 400 /var/lib/rabbitmq/.erlang.cookie
# 後臺啟動服務
$ sudo rabbitmq-server –detached&

3、ubuntu02、ubuntu03加入叢集

$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl join_cluster rabbit@ubuntu01
$ sudo rabbitmqctl start_app

4、檢視狀態

可以看到3個節點都是採用disc模式的

$ sudo rabbitmqctl cluster_status
Cluster status of node rabbit@ubuntu03
[{nodes,[{disc,[rabbit@ubuntu01,rabbit@ubuntu02,rabbit@ubuntu03]}]},
 {running_nodes,[rabbit@ubuntu01,rabbit@ubuntu02,rabbit@ubuntu03]},
 {cluster_name,<<"[email protected]
"
>>}, {partitions,[]}, {alarms,[{rabbit@ubuntu01,[]},{rabbit@ubuntu02,[]},{rabbit@ubuntu03,[]}]}]

5、修改ubuntu02節點為ram模式

$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl change_cluster_node_type ram
$ sudo rabbitmqctl start_app
$ sudo rabbitmqctl cluster_status
Cluster status of node rabbit@ubuntu02
[{nodes,[{disc,[rabbit@ubuntu03,rabbit@ubuntu01]},{ram,[rabbit@ubuntu02]}]},
 {running_nodes,[rabbit@ubuntu01,rabbit@ubuntu03,rabbit@ubuntu02]},
 {cluster_name,<<"[email protected]">>},
 {partitions,[]},
 {alarms,[{rabbit@ubuntu01,[]},{rabbit@ubuntu03,[]},{rabbit@ubuntu02,[]}]}]

可以看到成功修改了節點屬性

6、ubuntu03退出叢集(僅作嘗試)

ubuntu03執行:

$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl reset
$ sudo rabbitmqctl start_app

ubuntu01執行:

$ sudo rabbitmqctl forget_cluster_node rabbit@ubuntu03

注意事項

cookie在所有節點上必須完全一樣,同步時一定要注意。、

erlang是通過主機名來連線服務,必須保證各個主機名之間可以ping通。可以通過編輯/etc/hosts來手工新增主機名和IP對應關係。如果主機名ping不通,rabbitmq服務啟動會失敗。

如果queue是非持久化queue,則如果建立queue的那個節點失敗,傳送方和接收方可以建立同樣的queue繼續運作。但如果是持久化queue,則只能等建立queue的那個節點恢復後才能繼續服務。

在叢集元資料有變動的時候需要有disk node線上,但是在節點加入或退出的時候所有的disk node必須全部線上。如果沒有正確退出disk node,叢集會認為這個節點當掉了,在這個節點恢復之前不要加入其它節點。

RabbitMQ只要求叢集中至少有一個磁碟節點,其他節點都可以是記憶體節點。當節點加入或者離開叢集時,它們必須要將變更通知到至少一個磁碟節點。如果只有一個磁碟節點,磁碟節點奔潰後,叢集可以繼續路由訊息(即保持執行),但是直到該節點恢復之前,無法更改任何東西。通常在叢集中設定兩個磁碟節點。

RabbitMQ映象配置

在上述的叢集中,如果某個節點掛掉,整個叢集還是可以正常工作的,但是掛掉的那個節點的訊息就清空了。這種情況在生產環境中是不可接受的,所以需要用到映象功能,也就是主從配置。

在叢集中的任意一臺主機上執行:

$ sudo rabbitmqctl set_policy -p /test ha-allqueue "^" '{"ha-mode":"all"}'

這行命令在vhost名稱為/test建立了一個策略,策略名稱為ha-allqueue,策略模式為 all 即複製到所有節點,包含新增節點,策略正則表示式為 “^” 表示所有匹配所有佇列名稱。

配置映象佇列後,其中1臺節點失敗,佇列內容是不會丟失,如果整個叢集重啟,會恢復在disc中的持久化訊息。

HAProxy負載均衡

上述操作已經建立起一個高可用的訊息隊列了,那麼客戶端怎麼連線呢?

1、客戶端可以連線叢集中的任意一個節點,如果一個節點故障,客戶端自行重新連線到其他的可用節點;(不推薦,對客戶端不透明)
2、通過動態DNS,較短的ttl
3、HAProxy負載均衡

當然我們推薦第三種方式了:

在一臺新的主機上:192.168.56.114

1、安裝HAProxy(本次安裝了1.6.3版本):

$ sudo apt-get update
$ sudo apt-get install haproxy
$ haproxy -v       #檢視版本

2、修改配置檔案

配置檔案路徑:/etc/haproxy/haproxy.cfg
修改為如下:

##############全域性配置########################
global
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        chroot /var/lib/haproxy                #改變當前工作目錄
        stats socket /run/haproxy/admin.sock mode 660 level admin   #建立監控所用的套接字目錄
        stats timeout 30s       #超時時間
        user haproxy            #預設使用者
        group haproxy           #預設使用者組
        daemon                  #建立一個守護程序

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Default ciphers to use on SSL-enabled listening sockets.
        # For more information, see ciphers(1SSL). This list is from:
        #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3
#################預設配置#####################
defaults
        log     global
        mode    http            #預設的模式   tcp|http|health  可選,tcp是4層,http是7層,health至返回OK
        option  httplog         #http日誌模式
        option  dontlognull     # 啟用該項,日誌中將不會記錄空連線。所謂空連線就是在上游的負載均衡器
                                # 或者監控系統為了探測該 服務是否存活可用時,需要定期的連線或者獲取某
                                # 一固定的元件或頁面,或者探測掃描埠是否在監聽或開放等動作被稱為空連線;
                                # 官方文件中標註,如果該服務上游沒有其他的負載均衡器的話,建議不要使用
                                # 該引數,因為網際網路上的惡意掃描或其他動作就不會被記錄下來
        timeout connect 5000    #連線超時時間
        timeout client  50000   #客戶端連線超時時間
        timeout server  50000   #服務端連線超時時間
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

####################################################################
listen http_front
        bind 0.0.0.0:1080           #監聽埠  
        mode http
        stats refresh 30s           #統計頁面自動重新整理時間  
        stats uri /haproxy?stats    #統計頁面url  
        stats realm Haproxy Manager #統計頁面密碼框上提示文字  
        stats auth admin:admin      #統計頁面使用者名稱和密碼設定  
        #stats hide-version         #隱藏統計頁面上HAProxy的版本資訊

###############我把RabbitMQ的管理介面也放在HAProxy後面了#################
listen rabbitmq_admin
    bind 0.0.0.0:8004
    server node1 192.168.56.111:15672
    server node2 192.168.56.112:15672
    server node3 192.168.56.113:15672
####################################################################
listen rabbitmq_cluster
    bind 0.0.0.0:5672
    option tcplog
    mode tcp
    timeout client  3h
    timeout server  3h
    option          clitcpka
    balance roundrobin      #負載均衡演算法(#banlance roundrobin 輪詢,balance source 儲存session值,支援static-rr,leastconn,first,uri等引數)
    #balance url_param userid
    #balance url_param session_id check_post 64
    #balance hdr(User-Agent)
    #balance hdr(host)
    #balance hdr(Host) use_domain_only
    #balance rdp-cookie
    #balance leastconn
    #balance source //ip
    server   node1 192.168.56.111:5672 check inter 5s rise 2 fall 3   #check inter 2000 是檢測心跳頻率,rise 2是2次正確認為伺服器可用,fall 3是3次失敗認為伺服器不可用
    server   node2 192.168.56.112:5672 check inter 5s rise 2 fall 3
    server   node3 192.168.56.113:5672 check inter 5s rise 2 fall 3

3、重新啟動HAProxy

$ sudo service haproxy restart

4、驗證

叢集知識

如何選擇RabbitMQ的訊息儲存方式?

RabbitMQ對於queue中的message的儲存方式有兩種方式:disc和ram。如果採用disc,則需要對exchange/queue/delivery mode都要設定成durable模式。Disc方式的好處是當RabbitMQ失效了,message仍然可以在重啟之後恢復。而使用ram方式,RabbitMQ處理message的效率要高很多,ram和disc兩種方式的效率比大概是3:1。所以如果在有其它HA手段保障的情況下,選用ram方式是可以提高訊息佇列的工作效率的。

當訊息傳送的速率超過了RabbitMQ的處理能力時該怎麼辦?

RabbitMQ會自動減慢這個連線的速率,讓client端以為網路頻寬變小了,傳送訊息的速率會受限,從而達到流控的目的。 使用”rabbitmqctl list_connections”檢視連線,如果狀態為“flow”,則說明這個連線處於flow-control 狀態。

RabbitMQ叢集結構

RabbitMQ基於Erlang編寫,天然支援clustering。叢集是保證可靠性的一種方式,同時可以通過水平擴充套件以達到增加訊息吞吐能力的目的.
RabbitMQ叢集.png
上圖中是三個節點的RabbitMQ叢集,Exchange A的metadata資訊在所有節點上是一致的,queue的完整資訊則只在它建立的那個節點上。每個RabbitMQ節點通常以“[email protected]”表示,所以hostname在執行RabbitMQ的節點中很重要。注意:如果更改了hostname,需要重置RabbitMQ內部的資料庫,否則服務無法工作。

資料流動

RabbitMQ維護著四種類型的metadata: queue/exchange/binding/vhost,在叢集中這些資訊被同步到每個節點,因此當用戶訪問任何一個節點時,通過rabbitmqctl查詢到的queue/user/exchange等資訊都是相同的。

通常我們將這些資訊儲存到磁碟上,也就是查詢RabbitMQ狀態時的“disc”方式,以便叢集重啟時可以根據儲存的metadata資訊重建exchange等。

對於exchange來講,它的所有資訊就是一個exchange名字加上一個查詢表。查詢表中記錄了所有的queue binding。當message被髮送到exchange時,client連線的channel對routing key進行比對,根據binding進行正確的轉發。

對於Queue來講,雖然它的metadata在每個節點上都有,但只有在它被建立的那個RabbitMQ 節點上才具有完整的資訊:比如state/contents等,這個node被稱為此queue的owner node。其他節點只知道這個queue的metadata資訊和一個指向queue的owner node的指標。

如果一個client訪問RabbitMQ的節點上沒有需要的queue的完整資訊,RabbitMQ將根據這個指標將請求轉發到owner node。所以一個客戶端最好一直只和一個節點連線,這樣效率高一點。

Mnesia是RabbitMQ中的資料庫,它是內嵌在Erlang中的no-SQL資料庫。Exchange/Queue/Binding等的metadata資訊都儲存在mnesia的資料庫檔案中。關於RabbitMQ的叢集資訊也儲存在這裡。Rabbitmqctl的reset操作實際上就是清空了mnesia資料庫所在目錄的內容。