Centos6.9下RabbitMQ叢集部署記錄

散盡浮華發表於2017-12-09

 

之前簡單介紹了CentOS下單機部署RabbltMQ環境的操作記錄,下面詳細說下RabbitMQ叢集知識,RabbitMQ是用erlang開發的,叢集非常方便,因為erlang天生就是一門分散式語言,但其本身並不支援負載均衡。

Rabbit叢集模式大概分為以下三種:單一模式、普通模式、映象模式,其中:
1)單一模式:最簡單的情況,非叢集模式,沒什麼好說的。
2)普通模式:預設的叢集模式。
-> 對於Queue來說,訊息實體只存在於其中一個節點,A、B兩個節點僅有相同的後設資料,即佇列結構。
-> 當訊息進入A節點的Queue中後,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行訊息傳輸,把A中的訊息實體取出並經過B傳送給consumer。
-> 所以consumer應儘量連線每一個節點,從中取訊息。即對於同一個邏輯佇列,要在多個節點建立物理Queue。否則無論consumer連A或B,出口總在A,會產生瓶頸。
-> 該模式存在一個問題就是當A節點故障後,B節點無法取到A節點中還未消費的訊息實體。
-> 如果做了訊息持久化,那麼得等A節點恢復,然後才可被消費;如果沒有持久化的話,然後就沒有然後了。
3)映象模式:把需要的佇列做成映象佇列,存在於多個節點,屬於RabbitMQ的HA方案。
-> 該模式解決了上述問題,其實質和普通模式不同之處在於,訊息實體會主動在映象節點間同步,而不是在consumer取資料時臨時拉取。
-> 該模式帶來的副作用也很明顯,除了降低系統效能外,如果映象佇列數量過多,加之大量的訊息進入,叢集內部的網路頻寬將會被這種同步通訊大大消耗掉。
-> 所以在對可靠性要求較高的場合中適用於該模式(比如下面圖中介紹該種叢集模式)。

RabbitMQ叢集中的基本概念:
1)RabbitMQ的叢集節點包括記憶體節點、磁碟節點。顧名思義記憶體節點就是將所有資料放在記憶體,磁碟節點將資料放在磁碟。不過,如前文所述,如果在投遞訊息時,開啟了訊息的持久化,那麼即使是記憶體節點,資料還是安全的放在磁碟。
2)一個rabbitmq集 群中可以共享 user,vhost,queue,exchange等,所有的資料和狀態都是必須在所有節點上覆制的,一個例外是,那些當前只屬於建立它的節點的訊息佇列,儘管它們可見且可被所有節點讀取。rabbitmq節點可以動態的加入到叢集中,一個節點它可以加入到叢集中,也可以從叢集環叢集會進行一個基本的負載均衡。

RabbitMQ叢集中有兩種節點:
1)Ram記憶體節點:只儲存狀態到記憶體(一個例外的情況是:持久的queue的持久內容將被儲存到disk)
2)Disk磁碟節點:儲存狀態到記憶體和磁碟。
記憶體節點雖然不寫入磁碟,但是它執行比磁碟節點要好。RabbitMQ叢集中,只需要一個磁碟節點來儲存狀態就足夠了;如果叢集中只有記憶體節點,那麼不能停止它們,否則所有的狀態,訊息等都會丟失。

RabbitMQ叢集思路:
那麼具體如何實現RabbitMQ高可用,我們先搭建一個普通叢集模式,在這個模式基礎上再配置映象模式實現高可用,Rabbit叢集前增加一個反向代理,生產者、消費者通過反向代理訪問RabbitMQ叢集。

上圖中3個RabbitMQ執行在同一主機上,分別用不同的服務埠。當然在生產環境裡,多個RabbitMQ肯定是執行在不同的物理伺服器上,否則就失去了高可用的意義。

RabbitMQ叢集模式配置
該設計架構可以如下:在一個叢集裡,有3臺機器,其中1臺使用磁碟模式,另2臺使用記憶體模式。2臺記憶體模式的節點,無疑速度更快,因此客戶端(consumer、producer)連線訪問它們。而磁碟模式的節點,由於磁碟IO相對較慢,因此僅作資料備份使用,另外一臺作為反向代理。

配置RabbitMQ叢集非常簡單,只需要幾個命令,如下面範例,簡單說下配置的幾個步驟:
第一步:queue、kevintest1、kevintest2做為RabbitMQ叢集節點,分別安裝RabbitMq-Server ,安裝後分別啟動RabbitMq-server。

啟動命令  
# Rabbit-Server start 

第二步:在安裝好的三臺節點伺服器中,分別修改/etc/hosts檔案,指定queue、kevintest1、kevintest2的hosts。

172.16.3.32  queue
172.16.3.107 kevintest1
172.16.3.108 kevintest2
 
三臺節點的hostname要正確,主機名分別是queue、kevintest1、kevintest2,如果修改hostname,建議安裝rabbitmq前修改。請注意RabbitMQ叢集節點必須在同一個網段裡,
如果是跨廣域網效果就差。

第三步:設定每個節點Cookie

Rabbitmq的叢集是依賴於erlang的叢集來工作的,所以必須先構建起erlang的叢集環境。Erlang的叢集中各節點是通過一個magic cookie來實現的,這個cookie存放在 
/var/lib/rabbitmq/.erlang.cookie 中,檔案是400的許可權。所以必須保證各節點cookie保持一致,否則節點之間就無法通訊。
# ll /var/lib/rabbitmq/.erlang.cookie
-r-------- 1 rabbitmq rabbitmq 21 12月  6 00:40 /var/lib/rabbitmq/.erlang.cookie

將queue的/var/lib/rabbitmq/.erlang.cookie這個檔案,拷貝到kevintest1、kevintest2的同一位置(反過來亦可),該檔案是叢集節點進行通訊的驗證金鑰,所有
節點必須一致。拷完後重啟下RabbitMQ。複製好後別忘記還原.erlang.cookie的許可權,否則可能會遇到錯誤
# chmod 400 /var/lib/rabbitmq/.erlang.cookie 

設定好cookie後先將三個節點的rabbitmq重啟
# rabbitmqctl stop
# rabbitmq-server start

第四步:停止所有節點RabbitMq服務,然後使用detached引數獨立執行,這步很關鍵,尤其增加節點停止節點後再次啟動遇到無法啟動,都可以參照這個順序

[root@queue ~]# rabbitmqctl stop
[root@kevintest1 ~]# rabbitmqctl stop
[root@kevintest2 ~]# rabbitmqctl stop
 
[root@queue ~]# rabbitmq-server -detached
[root@kevintest1 ~]# rabbitmq-server -detached
[root@kevintest2 ~]# rabbitmq-server -detached
 
分別檢視下每個節點
[root@queue ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@queue ...
[{nodes,[{disc,[rabbit@queue]}]},
{running_nodes,[rabbit@queue]},
{partitions,[]}]
...done.
 
[root@kevintest1 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@kevintest1...
[{nodes,[{disc,[rabbit@kevintest1]}]},
{running_nodes,[rabbit@kevintest1]},
{partitions,[]}]
...done.

[root@kevintest2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@kevintest2...
[{nodes,[{disc,[rabbit@kevintest2]}]},
{running_nodes,[rabbit@kevintest2]},
{partitions,[]}]
...done.

第五步:將kevintest1、kevintest2作為記憶體節點與queue連線起來,在kevintest1上,執行如下命令:

[root@kevintest1 ~]# rabbitmqctl stop_app
[root@kevintest1 ~]# rabbitmqctl join_cluster --ram rabbit@queue   
[root@kevintest1 ~]# rabbitmqctl start_app

[root@kevintest2 ~]# rabbitmqctl stop_app
[root@kevintest2 ~]# rabbitmqctl join_cluster --ram rabbit@queue   #上面已經將kevintest1與queue連線,也可以直接將kevintest2與kevintest1連線,同樣而已加入叢集中
[root@kevintest2 ~]# rabbitmqctl start_app

1)上述命令先停掉rabbitmq應用,然後呼叫cluster命令,將kevintest1連線到,使兩者成為一個叢集,最後重啟rabbitmq應用。
2)在這個cluster命令下,kevintest1、kevintest2是記憶體節點,queue是磁碟節點(RabbitMQ啟動後,預設是磁碟節點)。
3)queue如果要使kevintest1或kevintest2在叢集裡也是磁碟節點,join_cluster 命令去掉--ram引數即可
#rabbitmqctl join_cluster rabbit@queue   
只要在節點列表裡包含了自己,它就成為一個磁碟節點。在RabbitMQ叢集裡,必須至少有一個磁碟節點存在。

第六步:在queue、kevintest1、kevintest2上,執行cluster_status命令檢視叢集狀態:

# rabbitmqctl cluster_status
Cluster status of node rabbit@queue ...
[{nodes,[{disc,[rabbit@queue]},{ram,[rabbit@kevintest2,rabbit@kevintest1]}]},
{running_nodes,[rabbit@kevintest2,rabbit@kevintest1,rabbit@queue]},
{partitions,[]}]
...done.

[root@kevintest1 rabbitmq]# rabbitmqctl cluster_status
Cluster status of node rabbit@kevintest1 ...
[{nodes,[{disc,[rabbit@queue]},{ram,[rabbit@kevintest2,rabbit@kevintest1]}]},
{running_nodes,[rabbit@kevintest2,rabbit@queue,rabbit@kevintest1]},
{partitions,[]}]
...done.
 
[root@kevintest2 rabbitmq]# rabbitmqctl cluster_status
Cluster status of node rabbit@kevintest2 ...
[{nodes,[{disc,[rabbit@queue]},{ram,[rabbit@kevintest2,rabbit@kevintest1]}]},
{running_nodes,[rabbit@kevintest1,rabbit@queue,rabbit@kevintest2]},
{partitions,[]}]
...done.

這時可以看到每個節點的叢集資訊,分別有兩個記憶體節點一個磁碟節點

第七步:往任意一臺叢集節點裡寫入訊息佇列,會複製到另一個節點上,我們看到兩個節點的訊息佇列數一致:

[root@kevintest2 ~]# rabbitmqctl list_queues -p hrsystem

Listing queues …
test_queue 10000
…done.

[root@kevintest1 ~]# rabbitmqctl list_queues -p hrsystem
Listing queues …
test_queue 10000
…done.
 
[root@queue ~]# rabbitmqctl list_queues -p hrsystem
Listing queues …
test_queue 10000
…done.
 
-p引數為vhost名稱

這樣RabbitMQ叢集就正常工作了,這種模式更適合非持久化佇列,只有該佇列是非持久的,客戶端才能重新連線到叢集裡的其他節點,並重新建立佇列。假如該佇列是持久化的,那麼唯一辦法是將故障節點恢復起來;為什麼RabbitMQ不將佇列複製到叢集裡每個節點呢?這與它的叢集的設計本意相沖突,叢集的設計目的就是增加更多節點時,能線性的增加效能(CPU、記憶體)和容量(記憶體、磁碟)。理由如下:當然RabbitMQ新版本叢集也支援佇列複製(有個選項可以配置)。比如在有五個節點的叢集裡,可以指定某個佇列的內容在2個節點上進行儲存,從而在效能與高可用性之間取得一個平衡。

=============清理RabbitMQ訊息佇列中的所有資料============

方法如下:
# rabbitmqctl list_queues    //檢視所有佇列資料
# rabbitmqctl stop_app      //要先關閉應用,否則不能清除
# rabbitmqctl reset
# rabbitmqctl start_app
# rabbitmqctl list_queues   //這時候看到listing 及queues都是空的

=========================================================================================

RabbitMQ叢集:
1)RabbitMQ broker叢集是多個erlang節點的邏輯組,每個節點執行rabbitmq應用,他們之間共享使用者、虛擬主機、佇列、exchange、繫結和執行時引數;
2)RabbitMQ叢集之間複製什麼資訊:除了message queue(存在一個節點,從其他節點都可見、訪問該佇列,要實現queue的複製就需要做queue的HA)之外,任何一個rabbitmq broker上的所有操作的data和state都會在所有的節點之間進行復制;
3)RabbitMQ訊息佇列是非常基礎的關鍵服務。本文3臺rabbitMQ伺服器構建broker叢集,1個master,2個slave。允許2臺伺服器故障而服務不受影響。

RabbitMQ叢集的目的
1)允許消費者和生產者在RabbitMQ節點崩潰的情況下繼續執行
2)通過增加更多的節點來擴充套件訊息通訊的吞吐量

RabbitMQ叢集執行的前提:
1)叢集所有節點必須執行相同的erlang及rabbitmq版本
2)hostname解析,節點之間通過域名相互通訊,本文為3個node的叢集,採用配置hosts的形式。

RabbitMQ埠及用途
1)5672 客戶端連線用途
2)15672 web管理介面
3)25672 叢集通訊用途

RabbitMQ叢集的搭建方式:
1)通過rabbitmqctl手工配置 (本文采用此方式)
2)通過配置檔案宣告
3)通過rabbitmq-autocluster外掛宣告
4)通過rabbitmq-clusterer外掛宣告

RabbitMQ叢集故障處理機制:
1)rabbitmq broker叢集允許個體節點down機,
2)對應叢集的的網路分割槽問題( network partitions)

RabbitMQ叢集推薦用於LAN環境,不適用WAN環境;要通過WAN連線broker,Shovel or Federation外掛是最佳的解決方案;Shovel or Federation不同於叢集。

RabbitMQ叢集的節點執行模式:
為保證資料永續性,目前所有node節點跑在disk模式,如果今後壓力大,需要提高效能,考慮採用ram模式

RabbitMQ節點型別
1)RAM node:記憶體節點將所有的佇列、交換機、繫結、使用者、許可權和vhost的後設資料定義儲存在記憶體中,好處是可以使得像交換機和佇列宣告等操作更加的快速。
2)Disk node:將後設資料儲存在磁碟中,單節點系統只允許磁碟型別的節點,防止重啟RabbitMQ的時候,丟失系統的配置資訊。

問題說明:
RabbitMQ要求在叢集中至少有一個磁碟節點,所有其他節點可以是記憶體節點,當節點加入或者離開叢集時,必須要將該變更通知到至少一個磁碟節點。
如果叢集中唯一的一個磁碟節點崩潰的話,叢集仍然可以保持執行,但是無法進行其他操作(增刪改查),直到節點恢復。

解決方案:設定兩個磁碟節點,至少有一個是可用的,可以儲存後設資料的更改。

RabbitMQ叢集節點之間是如何相互認證的:
1)通過Erlang Cookie,相當於共享祕鑰的概念,長度任意,只要所有節點都一致即可。
2)rabbitmq server在啟動的時候,erlang VM會自動建立一個隨機的cookie檔案。cookie檔案的位置是/var/lib/rabbitmq/.erlang.cookie 或者 /root/.erlang.cookie,為保證cookie的完全一致,採用從一個節點copy的方式。

Erlang Cookie是保證不同節點可以相互通訊的金鑰,要保證叢集中的不同節點相互通訊必須共享相同的Erlang Cookie。具體的目錄存放在/var/lib/rabbitmq/.erlang.cookie。
說明:這就要從rabbitmqctl命令的工作原理說起,RabbitMQ底層是通過Erlang架構來實現的,所以rabbitmqctl會啟動Erlang節點,並基於Erlang節點來使用Erlang系統連線RabbitMQ節點,在連線過程中需要正確的Erlang Cookie和節點名稱,Erlang節點通過交換Erlang Cookie以獲得認證。

=======以下記錄CentOS6.9下RabbitMQ叢集部署過程=======

叢集機器資訊:
rabbitmq01.kevin.cn      192.168.1.40 
rabbitmq02.kevin.cn      192.168.1.41
rabbitmq03.kevin.cn      192.168.1.42
 
1)設定hosts主機解析,rabbitmq 叢集通訊用途,所有節點配置相同。
[root@rabbitmq01 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.40  rabbitmq01.kevin.cn
192.168.1.41  rabbitmq02.kevin.cn
192.168.1.42  rabbitmq03.kevin.cn
 
其他兩個節點的hosts配置一致。
 
2)三臺節點伺服器上都要部署rabbitmq環境,可以參考:http://www.cnblogs.com/kevingrace/p/7693042.html
前臺執行rabbitmq服務: 
# /etc/init.d/rabbitmq-server start  (使用者關閉連線後,自動結束程式)
或者
# rabbitmq-server start    
 
設定開機啟動
# chkconfig rabbitmq-server on
  
後臺執行rabbitmq服務:
# rabbitmq-server -detached
 
# lsof -i:5672
# lsof -i:15672
# lsof -i:25672
 
檢視各節點狀態:
# rabbitmqctl status
或者
# /etc/init.d/rabbitmq-server status
 
3)設定節點間認證的cookie。可以把其中一個節點(比如rabbitmq01)的檔案使用scp拷貝到其他兩個節點上
[root@rabbitmq01 ~]# cat /var/lib/rabbitmq/.erlang.cookie
FXQTFVXIUWEBZRLXFQOZ
[root@rabbitmq02 ~]# cat /var/lib/rabbitmq/.erlang.cookie
FXQTFVXIUWEBZRLXFQOZ
[root@rabbitmq03 ~]# cat /var/lib/rabbitmq/.erlang.cookie
FXQTFVXIUWEBZRLXFQOZ
 
同步完cookie之後,重啟rabbitmq-server。
# /etc/init.d/rabbitmq-server restart
 
4)為了把叢集中的3個節點聯絡起來,可以將其中兩個節點加入到另一個節點中。
比如:將rabbitmq01、rabbitmq03分別加入到叢集rabbitmq02中,其中rabbitmq01和rabbitmq02節點為記憶體節點。rabbitmq02為磁碟節點。
 
注意:rabbitmqctl stop_app ---僅關閉應用,節點不被關閉
 
[root@rabbitmq01 ~]# rabbitmqctl stop_app
[root@rabbitmq01 ~]# rabbitmqctl join_cluster --ram rabbit@rabbitmq02
[root@rabbitmq01 ~]# rabbitmqctl start_app
 
[root@rabbitmq03 ~]# rabbitmqctl stop_app
[root@rabbitmq03 ~]# rabbitmqctl join_cluster --ram rabbit@rabbitmq02
[root@rabbitmq03 ~]# rabbitmqctl start_app
 
檢視RabbitMQ叢集情況(三個節點檢視的結果一樣)
[root@rabbitmq01 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbitmq01 ...
[{nodes,[{disc,[rabbit@rabbitmq02]},
         {ram,[rabbit@rabbitmq03,rabbit@rabbitmq01]}]},
 {running_nodes,[rabbit@rabbitmq03,rabbit@rabbitmq02,rabbit@rabbitmq01]},
 {cluster_name,<<"rabbit@rabbitmq02.kevin.cn">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbitmq03,[]},
          {rabbit@rabbitmq02,[]},
          {rabbit@rabbitmq01,[]}]}]
 
RabbitMQ叢集的名字預設是第一個節點的名字,比如上面叢集的名字是rabbitmq01。
 
修改RabbitMQ叢集的名字kevinmq
# rabbitmqctl set_cluster_name kevinmq
# rabbitmqctl cluster_status
 
重啟叢集:
# rabbitmqctl stop
# rabbitmq-server -detached
# rabbitmqctl cluster_status    //觀察叢集的執行狀態變化
 
5)重要資訊:
當整個叢集down掉時,最後一個down機的節點必須第一個啟動到線上狀態,如果不是這樣,節點會等待30s等最後的磁碟節點恢復狀態,然後失敗。
如果最後下線的節點不能上線,可以通過forget_cluster_node 指令來踢出叢集。
 
如果所有的節點不受控制的同時當機,比如掉電,會進入所有的節點都會認為其他節點比自己當機的要晚,即自己先當機,這種情況下可以使用
force_boot指令來啟動一個節點。
 
6)打破叢集:
當一個節點不屬於這個叢集的時候,需要及時踢出,可以通過本地或者遠端的方式
# rabbitmqctl stop_app
# rabbitmqctl reset
# rabbitmqctl start_app
 
這樣再次檢視RabbitMQ叢集的時候,該節點就不會在這裡面了
# rabbitmqctl cluster_status
 
7)客戶端連線叢集測試
通過web管理頁面進行建立佇列、釋出訊息、建立使用者、建立policy等。
http://192.168.1.41:15672/

或者通過rabbitmqadmin命令列來測試
[root@rabbitmq02 ~]# wget https://192.168.1.41:15672/cli/rabbitmqadmin
[root@rabbitmq02 ~]# chmod +x rabbitmqadmin
[root@rabbitmq02 ~]# mv rabbitmqadmin /usr/sbin/

Declare an exchange
[root@rabbitmq02 ~]# rabbitmqadmin declare exchange name=my-new-exchange type=fanout
exchange declared
Declare a queue, with optional parameters

[root@rabbitmq02 ~]# rabbitmqadmin declare queue name=my-new-queue durable=false
queue declared
Publish a message

[root@rabbitmq02 ~]# rabbitmqadmin publish exchange=my-new-exchange routing_key=test payload="hello, world"
Message published
And get it back

[root@rabbitmq02 ~]# rabbitmqadmin get queue=test requeue=false
+-------------+----------+---------------+--------------+------------------+-------------+
| routing_key | exchange | message_count | payload      | payload_encoding | redelivered |
+-------------+----------+---------------+--------------+------------------+-------------+
| test        |          | 0             | hello, world | string           | False       |
+-------------+----------+---------------+--------------+------------------+-------------+

測試後發現問題問題:
[root@rabbitmq01 ~]# rabbitmqctl stop_app
[root@rabbitmq01 ~]# rabbitmqctl stop
在stop_app或者stop掉broker之後在rabbitmq01節點的上佇列已經不可用了,重啟rabbitmq01的app或broker之後,雖然叢集工作正常,但rabbitmq01上佇列中訊息會被清空(queue還是存在的)

對於生產環境而已,這肯定是不可接受的,如果不能保證佇列的高可用,那麼做叢集的意義也不太大了,還好rabbitmq支援Highly Available Queues,下面介紹下queue的HA。

=================Queue HA配置===============

預設情況下,RabbitMQ叢集中的佇列存在於叢集中的單個節點上,這要看建立佇列時宣告在那個節點上建立,而exchange和binding則預設存在於叢集中所有節點。
佇列可以通過映象來提高可用性,HA依賴rabbitmq cluster,所以佇列映象也不適合WAN部署,每個被映象的佇列包含一個master和一個或者多個slave,當master
因任何原因故障時,最老的slave被提升為新的master。釋出到佇列的訊息被複制到所有的slave上,消費者無論連線那個node,都會連線到master;如果master確
認要刪除訊息,那麼所有slave就會刪除佇列中訊息。佇列映象可以提供queue的高可用性,但不能分擔負載,因為所有參加的節點都做所有的工作。

1. 配置佇列映象
通過policy來配置映象,策略可在任何時候建立,比如先建立一個非映象的佇列,然後在映象,反之亦然。
映象佇列和非映象佇列的區別是非映象佇列沒有slaves,執行速度也比映象佇列快。

設定策略,然後設定ha-mode,3種模式:all、exactly、nodes。
每個佇列都有一個home node,叫做queue master node

1)設定policy,以ha.開頭的佇列將會被映象到叢集其他所有節點,一個節點掛掉然後重啟後需要手動同步佇列訊息

# rabbitmqctl set_policy ha-all-queue "^ha\." '{"ha-mode":"all"}'

2)設定policy,以ha.開頭的佇列將會被映象到叢集其他所有節點,一個節點掛掉然後重啟後會自動同步佇列訊息(生產環境採用這個方式)

# rabbitmqctl set_policy ha-all-queue "^ha\." '{"ha-mode":"all","ha-sync-mode":"automatic"}'

2. 問題:
配置映象佇列後,其中1臺節點失敗,佇列內容是不會丟失,如果整個叢集重啟,佇列中的訊息內容仍然丟失,如何實現佇列訊息內容持久化那?
叢集節點跑在disk模式,建立見訊息的時候也宣告瞭持久化,為什麼還是不行那?

因為建立訊息的時候需要指定訊息是否持久化,如果啟用了訊息的持久化的話,重啟叢集訊息也不會丟失了,前提是建立的佇列也應該是建立的持久化佇列。

客戶端連線rabbitMQ叢集服務的方式:
1)客戶端可以連線叢集中的任意一個節點,如果一個節點故障,客戶端自行重新連線到其他的可用節點;(不推薦,對客戶端不透明)
2)通過動態DNS,較短的ttl
3)通過HA+4層負載均衡器(比如haproxy+keepalived)

==========Haproxy+keepalived的部署===============
訊息佇列作為公司的關鍵基礎服務,為給客戶端提供穩定、透明的rabbitmq服務,現通過Haproxy+keepalived構建高可用的rabbitmq統一入口,及基本的負載均衡服務。
為簡化安裝配置,現採用yum的方式安裝haproxy和keepalived,可參考 基於keepalived+nginx部署強健的高可用7層負載均衡方案。

在兩臺兩臺伺服器部署haproxy+Keepalived環境,部署過程一樣。
haroxy01.kevin.cn      192.168.1.43
haroxy02.kevin.cn      192.168.1.44

1)安裝
[root@haproxy01 ~]# yum install haproxy keepalived -y
[root@haproxy01 ~]# /etc/init.d/keepalived start

2)設定關鍵服務開機自啟動
[root@haproxy01 ~]# chkconfig --list|grep haproxy
[root@haproxy01 ~]# chkconfig haproxy on
[root@haproxy01 ~]# chkconfig --list|grep haproxy

3) 配置將haproxy的log記錄到 /var/log/haproxy.log
[root@haproxy01 ~]# more /etc/rsyslog.d/haproxy.conf
$ModLoad imudp
$UDPServerRun 514
local0.* /var/log/haproxy.log

[root@haproxy01 ~]# /etc/init.d/rsyslog restart

4)haproxy的配置,2臺機器上的配置完全相同
[root@haproxy01 ~]# more /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Example configuration for a possible web application. See the
# full configuration options online.
#
# https://haproxy.1wt.eu/download/1.4/doc/configuration.txt
#
#---------------------------------------------------------------------


#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
# to have these messages end up in /var/log/haproxy.log you will
# need to:
#
# 1) configure syslog to accept network log events. This is done
# by adding the '-r' option to the SYSLOGD_OPTIONS in
# /etc/sysconfig/syslog
#
# 2) configure local2 events to go to the /var/log/haproxy.log
# file. A line like the following can be added to
# /etc/sysconfig/syslog
#
# local2.* /var/log/haproxy.log
#
log 127.0.0.1 local2 notice

chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon

# turn on stats unix socket
stats socket /var/lib/haproxy/stats

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
mode tcp
option tcplog
option dontlognull
option http-server-close
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 3000

###haproxy statistics monitor by laijingli 20160222
listen statics 0.0.0.0:8888
mode http
log 127.0.0.1 local0 debug
transparent
stats refresh 60s
stats uri / haproxy-stats
stats realm Haproxy \ statistic
stats auth laijingli:xxxxx

#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend kevin_rabbitMQ_cluster_frontend
mode tcp
option tcpka
log 127.0.0.1 local0 debug
bind 0.0.0.0:5672
use_backend kevin_rabbitMQ_cluster_backend

frontend kevin_rabbitMQ_cluster_management_frontend
mode tcp
option tcpka
log 127.0.0.1 local0 debug
bind 0.0.0.0:15672
use_backend kevin_rabbitMQ_cluster_management_backend


#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend kevin_rabbitMQ_cluster_backend
balance roundrobin
server rabbitmq01.kevin.cn 192.168.1.40:5672 check inter 3s rise 1 fall 2
server rabbitmq02.kevin.cn 192.168.1.41:5672 check inter 3s rise 1 fall 2
server rabbitmq03.kevin.cn 192.168.1.42:5672 check inter 3s rise 1 fall 2


backend kevin_rabbitMQ_cluster_management_backend
balance roundrobin
server rabbitmq01.kevin.cn 192.168.1.40:15672 check inter 3s rise 1 fall 2
server rabbitmq02.kevin.cn 192.168.1.41:15672 check inter 3s rise 1 fall 2
server rabbitmq03.kevin.cn 192.168.1.42:15672 check inter 3s rise 1 fall 2

5)keepalived配置,特別注意2臺伺服器上的keepalived配置不一樣。
=======================先看下haroxy01.kevin.cn機器上的配置===========================
[root@haproxy01 ~]# more /etc/keepalived/keepalived.conf
global_defs {
notification_email {
wangshibo@kevin.cn
102533678@qq.com
}
notification_email_from notice@kevin.cn
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id haproxy43      ## xxhaproxy101 on master , xxhaproxy102 on backup
}


###simple check with killall -0 which is less expensive than pidof to verify that nginx is running
vrrp_script chk_nginx {
script "killall -0 nginx"
interval 1
weight 2
fall 2
rise 1
}


vrrp_instance KEVIN_GATEWAY {
state MASTER              ## MASTER on master , BACKUP on backup
interface em1
virtual_router_id 101         ## KEVIN_GATEWAY virtual_router_id
priority 200            ## 200 on master , 199 on backup
advert_int 1
###採用單播通訊,避免同一個區域網中多個keepalived組之間的相互影響
unicast_src_ip 192.168.1.43     ##本機ip
unicast_peer {
192.168.1.44     ##對端ip
}
authentication {
auth_type PASS
auth_pass 123456
}
virtual_ipaddress {
192.168.1.45                  ## VIP
}
###如果只有一塊網路卡的話監控網路介面就沒有必要了
#track_interface {
# em1
#}
track_script {
chk_nginx
}
###狀態切換是傳送郵件通知,本機記錄log,後期會觸發簡訊通知
notify_master /usr/local/bin/keepalived_notify.sh notify_master
notify_backup /usr/local/bin/keepalived_notify.sh notify_backup
notify_fault /usr/local/bin/keepalived_notify.sh notify_fault
notify /usr/local/bin/keepalived_notify.sh notify
smtp_alert
}

###simple check with killall -0 which is less expensive than pidof to verify that haproxy is running
vrrp_script chk_haproxy {
script "killall -0 haproxy"
interval 1
weight 2
fall 2
rise 1
}
vrrp_instance kevin_rabbitMQ_GATEWAY {
state BACKUP          ## MASTER on master , BACKUP on backup
interface em1
virtual_router_id 111 ## kevin_rabbitMQ_GATEWAY virtual_router_id
priority 199         ## 200 on master , 199 on backup
advert_int 1
###採用單播通訊,避免同一個區域網中多個keepalived組之間的相互影響
unicast_src_ip 192.168.1.43      ##本機ip
unicast_peer {
192.168.1.44        ##對端ip
}
authentication {
auth_type PASS
auth_pass 123456
}
virtual_ipaddress {
192.168.1.46        ## VIP
}
###如果只有一塊網路卡的話監控網路介面就沒有必要了
#track_interface {
# em1
#}
track_script {
chk_haproxy
}
###狀態切換是傳送郵件通知,本機記錄log,後期會觸發簡訊通知
notify_master /usr/local/bin/keepalived_notify_for_haproxy.sh notify_master
notify_backup /usr/local/bin/keepalived_notify_for_haproxy.sh notify_backup
notify_fault /usr/local/bin/keepalived_notify_for_haproxy.sh notify_fault
notify /usr/local/bin/keepalived_notify_for_haproxy.sh notify
smtp_alert
}


=============================再看下haroxy02.kevin.cn機器上的配置==========================
[root@haproxy02 ~]# more /etc/keepalived/keepalived.conf
global_defs {
notification_email {
wangshibo@kevin.cn
102533678@qq.com
}
notification_email_from notice@kevin.cn
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id haproxy44     ## xxhaproxy101 on master , xxhaproxy102 on backup
}

###simple check with killall -0 which is less expensive than pidof to verify that nginx is running
vrrp_script chk_nginx {
script "killall -0 nginx"
interval 1
weight 2
fall 2
rise 1
}

vrrp_instance KEVIN_GATEWAY {
state BACKUP         ## MASTER on master , BACKUP on backup
interface em1
virtual_router_id 101       ## KEVIN_GATEWAY virtual_router_id
priority 199      ## 200 on master , 199 on backup
advert_int 1
###採用單播通訊,避免同一個區域網中多個keepalived組之間的相互影響
unicast_src_ip 192.168.1.44     ##本機ip
unicast_peer {
192.168.1.43       ##對端ip
}
authentication {
auth_type PASS
auth_pass YN_API_HA_PASS
}
virtual_ipaddress {
192.168.1.45     ## VIP
}
###如果只有一塊網路卡的話監控網路介面就沒有必要了
#track_interface {
# em1
#}
track_script {
chk_nginx
}
###狀態切換是傳送郵件通知,本機記錄log,後期會觸發簡訊通知
notify_master /usr/local/bin/keepalived_notify.sh notify_master
notify_backup /usr/local/bin/keepalived_notify.sh notify_backup
notify_fault /usr/local/bin/keepalived_notify.sh notify_fault
notify /usr/local/bin/keepalived_notify.sh notify
smtp_alert
}

###simple check with killall -0 which is less expensive than pidof to verify that haproxy is running
vrrp_script chk_haproxy {
script "killall -0 haproxy"
interval 1
weight 2
fall 2
rise 1
}
vrrp_instance kevin_rabbitMQ_GATEWAY {
state MASTER        ## MASTER on master , BACKUP on backup
interface em1
virtual_router_id 111       ## kevin_rabbitMQ_GATEWAY virtual_router_id
priority 200       ## 200 on master , 199 on backup
advert_int 1
###採用單播通訊,避免同一個區域網中多個keepalived組之間的相互影響
unicast_src_ip 192.168.1.44      ##本機ip
unicast_peer {
192.168.1.43      ##對端ip
}
authentication {
auth_type PASS
auth_pass YN_MQ_HA_PASS
}
virtual_ipaddress {
192.168.1.46      ## VIP
}
###如果只有一塊網路卡的話監控網路介面就沒有必要了
#track_interface {
# em1
#}
track_script {
chk_haproxy
}
###狀態切換是傳送郵件通知,本機記錄log,後期會觸發簡訊通知
notify_master /usr/local/bin/keepalived_notify_for_haproxy.sh notify_master
notify_backup /usr/local/bin/keepalived_notify_for_haproxy.sh notify_backup
notify_fault /usr/local/bin/keepalived_notify_for_haproxy.sh notify_fault
notify /usr/local/bin/keepalived_notify_for_haproxy.sh notify
smtp_alert
}


配置中用到的通知指令碼,2臺haproxy伺服器上完全一樣:
[root@haproxy01 ~]# more /usr/local/bin/keepalived_notify.sh
#!/bin/bash
###keepalived notify script for record ha state transtion to log files

###將將狀態轉換過程記錄到log,便於排錯
logfile=/var/log/keepalived.notify.log
echo --------------- >> $logfile
echo `date` [`hostname`] keepalived HA role state transition: $1 $2 $3 $4 $5 $6 >> $logfile

###將狀態轉換記錄到nginx的檔案,便於通過web檢視ha狀態(一定注意不要開放到公網)
echo `date` `hostname` $1 $2 $3 $4 $5 $6 "
" > /usr/share/nginx/html/index_for_nginx.html

###將nginx api和rabbitmq的ha log記錄到同一個檔案裡
cat /usr/share/nginx/html/index_for* > /usr/share/nginx/html/index.html


6)haproxy監控頁面。
訪問地址http://192.168.1.43:8888

7)檢視keepalived中高可用服務執行在那臺伺服器上
https://192.168.1.43

8)通過VIP訪問rabbitMQ服務
http://192.168.1.46:5672

9)其他問題
rabbitmq服務客戶端使用規範
1)使用vhost來隔離不同的應用、不同的使用者、不同的業務組
2)訊息持久化,exchange、queue、message等持久化需要在客戶端宣告指定

相關文章