rabbitmq 原理、叢集、基本運維操作、常見故障處理

程式設計三分鐘發表於2019-04-21

歡迎訪問我的個人主頁 qupzhi.com ,轉載請註明出處。

摘要

本次學習主要針對運維人員,和對rabbitmq不熟悉的開發人員。通過本次學習你將掌握rabbitmq 的基本原理、叢集、基本運維操作、常見故障處理

用時:25 分鐘

原理與概念

用時:9 分鐘

簡介

AMQP,即Advanced Message Queuing Protocol,高階訊息佇列協議,是應用層協議的一個開放標準,為面向訊息的中介軟體設計。訊息中介軟體主要用於元件之間的解耦,訊息的傳送者無需知道訊息使用者的存在,反之亦然。

AMQP的主要特徵是面向訊息、佇列、路由(包括點對點和釋出/訂閱)、可靠性、安全。 RabbitMQ是一個開源的AMQP實現,伺服器端用Erlang語言編寫,支援多種客戶端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支援AJAX。用於在分散式系統中儲存轉發訊息,在易用性、擴充套件性、高可用性等方面表現不俗。

解決的問題

RabbitMQ就是當前最主流的訊息中介軟體之一。

  • 兩個(多個)系統間需要通過定時任務來同步某些資料
  • 異構系統的不同程式間相互呼叫、通訊的問題

Queue

Queue(佇列)是RabbitMQ的內部物件,用於儲存訊息,用下圖表示。

rabbitmq 原理、叢集、基本運維操作、常見故障處理
RabbitMQ中的訊息都只能儲存在Queue中,生產者(下圖中的P)生產訊息並最終投遞到Queue中,消費者(下圖中的C)可以從Queue中獲取訊息並消費。
rabbitmq 原理、叢集、基本運維操作、常見故障處理

多個消費者可以訂閱同一個Queue,這時Queue中的訊息會被平均分攤給多個消費者進行處理,而不是每個消費者都收到所有的訊息並處理。

rabbitmq 原理、叢集、基本運維操作、常見故障處理

技術術語

  • Broker:簡單來說就是訊息佇列伺服器實體。
  • producer:訊息生產者,就是投遞訊息的程式。
  • consumer:訊息消費者,就是接受訊息的程式。
  • vhost:虛擬主機,一個broker裡可以開設多個vhost,用作許可權分離,把不同的系統使用的rabbitmq區分開,共用一個訊息佇列伺服器,但看上去就像各自在用不用的rabbitmq伺服器一樣。
  • Connection:一個網路連線,比如TCP/IP套接字連線。
  • channel:訊息通道,是建立在真實的TCP連線內的虛擬連線(是我們與RabbitMQ打交道的最重要的一個介面)。僅僅建立了客戶端到Broker之間的連線後,客戶端還是不能傳送訊息的,需要為每一個Connection建立Channel,AMQP協議規定只有通過Channel才能執行AMQP的命令。AMQP的命令都是通過通道傳送出去的(我們大部分的業務操作是在Channel這個介面中完成的,包括定義Queue、定義Exchange、繫結Queue與Exchange、釋出訊息等。)。每條通道都會被指派一個唯一ID。在客戶端的每個連線裡,可建立多個channel,每個channel代表一個會話任務,理論上無限制,減少TCP建立和銷燬的開銷,實現共用TCP的效果。之所以需要Channel,是因為TCP連線的建立和釋放都是十分昂貴的,如果一個客戶端每一個執行緒都需要與Broker互動,如果每一個執行緒都建立一個TCP連線,暫且不考慮TCP連線是否浪費,就算作業系統也無法承受每秒建立如此多的TCP連線。 注1:一個生產者或一個消費者與MQ伺服器之間只有一條TCP連線 注2:RabbitMQ建議客戶端執行緒之間不要共用Channel,至少要保證共用Channel的執行緒傳送訊息必須是序列的,但是建議儘量共用Connection。
  • Exchange:訊息交換機,生產者不是直接將訊息投遞到Queue中的,實際上是生產者將訊息傳送到Exchange(交換器,下圖中的X),由Exchange將訊息路由到一個或多個Queue中(或者丟棄)。
    exchange
  • Exchange Types RabbitMQ常用的Exchange Type有fanout、direct、topic、headers這四種(AMQP規範裡還提到兩種Exchange Type,分別為system與自定義,這裡不予以描述),之後會分別進行介紹。
  • Queue:訊息佇列載體,每個訊息都會被投入到一個或多個佇列。
  • Binding:繫結,它的作用就是把exchange和queue按照路由規則繫結起來,這樣RabbitMQ就知道如何正確地將訊息路由到指定的Queue了。
    binding
  • Routing Key:路由關鍵字,生產者在將訊息傳送給Exchange的時候,一般會指定一個routing key,來指定這個訊息的路由規則,而這個routing key需要與Exchange Type及binding key聯合使用才能最終生效。
    rabbitmq 原理、叢集、基本運維操作、常見故障處理
    在Exchange Type與binding key固定的情況下(在正常使用時一般這些內容都是固定配置好的),我們的生產者就可以在傳送訊息給Exchange時,通過指定routing key來決定訊息流向哪裡。
  • Prefetch count 前面我們講到如果有多個消費者同時訂閱同一個Queue中的訊息,Queue中的訊息會被平攤給多個消費者。這時如果每個訊息的處理時間不同,就有可能會導致某些消費者一直在忙,而另外一些消費者很快就處理完手頭工作並一直空閒的情況。我們可以通過設定prefetchCount來限制Queue每次傳送給每個消費者的訊息數,比如我們設定prefetchCount=1,則Queue每次給每個消費者傳送一條訊息;消費者處理完這條訊息後Queue會再給該消費者傳送一條訊息。
    prefetchCount

prefetchCount

訊息佇列的使用過程

在AMQP模型中,Exchange是接受生產者訊息並將訊息路由到訊息佇列的關鍵元件。ExchangeType和Binding決定了訊息的路由規則。所以生產者想要傳送訊息,首先必須要宣告一個Exchange和該Exchange對應的Binding。

在Rabbit MQ中,宣告一個Exchange需要三個引數:ExchangeName,ExchangeType和Durable。ExchangeName是該Exchange的名字,該屬性在建立Binding和生產者通過publish推送訊息時需要指定。ExchangeType,指Exchange的型別,在RabbitMQ中,有三種型別的Exchange:direct ,fanout和topic,不同的Exchange會表現出不同路由行為。Durable是該Exchange的持久化屬性,這個會在訊息持久化章節討論。

宣告一個Exchange

宣告一個Binding需要提供一個QueueName,ExchangeName和BindingKey。

宣告一個Binding
下面是訊息傳送的過程
rabbitmq 原理、叢集、基本運維操作、常見故障處理

  1. 建立連線Connection。由producer和consumer建立連線,連線到broker的物理節點上。
  2. 建立訊息Channel。Channel是建立在Connection之上的,一個Connection可以建立多個Channel。producer連線Virtual Host 建立Channel,Consumer連線到相應的queue上建立Channel。
  3. 傳送訊息。由Producer傳送訊息到Broker中的Exchange中。
  4. 路由轉發。生產者Producer在傳送訊息時,都需要指定一個RoutingKey和Exchange,Exchange收到訊息後可以看到訊息中指定的RoutingKey,再根據當前Exchange的ExchangeType,按一定的規則將訊息轉發到相應的queue中去。
  5. 訊息接收。Consumer會監聽相應的queue,一旦queue中有可以消費的訊息,queue就將訊息傳送給Consumer端。
  6. 訊息確認。當Consumer完成某一條訊息的處理之後,需要傳送一條ACK訊息給對應的Queue。Queue收到ACK資訊後,才會認為訊息處理成功,並將訊息從Queue中移除;如果在對應的Channel斷開後,Queue沒有收到這條訊息的ACK資訊,該訊息將被髮送給另外的Channel。至此一個訊息的傳送接收流程走完了。訊息的確認機制提高了通訊的可靠性。

exchange 與 Queue 的路由機制

exchange type
exchange 將訊息傳送到哪一個queue是由exchange type 和bing 規則決定的,目前常用的有3種exchange,Direct exchange, Fanout exchange, Topic exchange 。

  • Direct exchange 直接轉發路由,其實現原理是通過訊息中的routkey,與queue 中的routkey 進行比對,若二者匹配,則將訊息傳送到這個訊息佇列。通常使用這個。

    Direct
    以上圖的配置為例,我們以routingKey=”error”傳送訊息到Exchange,則訊息會路由到Queue1(amqp.gen-S9b…,這是由RabbitMQ自動生成的Queue名稱)和Queue2(amqp.gen-Agl…);如果我們以routingKey=”info”或routingKey=”warning”來傳送訊息,則訊息只會路由到Queue2。如果我們以其他routingKey傳送訊息,則訊息不會路由到這兩個Queue中。

  • Fanout exchange 複製分發路由,該路由不需要routkey,當exchange收到訊息後,將訊息複製多份轉發給與自己繫結的訊息佇列。

Fanout
上圖中,生產者(P)傳送到Exchange(X)的所有訊息都會路由到圖中的兩個Queue,並最終被兩個消費者(C1與C2)消費。

  • topic exchange 通配路由,是direct exchange的萬用字元模式,訊息中的routkey可以寫成通配的模式,exchange支援“#”和“*” 的通配。收到訊息後,將訊息轉發給所有符合匹配表示式的queue。
    topic
    以上圖中的配置為例,routingKey=”quick.orange.rabbit”的訊息會同時路由到Q1與Q2,routingKey=”lazy.orange.fox”的訊息會路由到Q1,routingKey=”lazy.brown.fox”的訊息會路由到Q2,routingKey=”lazy.pink.rabbit”的訊息會路由到Q2(只會投遞給Q2一次,雖然這個routingKey與Q2的兩個bindingKey都匹配);routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit”的訊息將會被丟棄,因為它們沒有匹配任何bindingKey。

需要注意的一點只有queue具有 保持訊息的功能,exchange不能儲存訊息。

  • headers headers型別的Exchange不依賴於routing key與binding key的匹配規則來路由訊息,而是根據傳送的訊息內容中的headers屬性進行匹配。 在繫結Queue與Exchange時指定一組鍵值對;當訊息傳送到Exchange時,RabbitMQ會取到該訊息的headers(也是一個鍵值對的形式),對比其中的鍵值對是否完全匹配Queue與Exchange繫結時指定的鍵值對;如果完全匹配則訊息會路由到該Queue,否則不會路由到該Queue。 該型別的Exchange沒有用到過(不過也應該很有用武之地),所以不做介紹。

durability 持久化與非持久化佇列

如何識別

  1. 如何識別? 如上圖,在Features欄位裡有一個D,就是持久化佇列,英文durable(持久的)
  2. 持久化佇列和非持久化佇列的區別是什麼? 持久化佇列會被儲存在磁碟中,固定並持久的儲存,當Rabbit服務重啟後,該佇列會保持原來的狀態在RabbitMQ中被管理,而非持久化佇列不會被儲存在磁碟中,Rabbit服務重啟後佇列就會消失。
  3. 如何選擇? 如果需要佇列的完整性,資料在佇列中的儲存是必須不允許丟失的,那麼可以使用持久化。而當需要獲取的資訊是實時的,或者是隨機的資訊,不需要資訊的精確性或完整性,但是追求獲取效能,可以選擇非持久化佇列。

分散式叢集架構和高可用性

用時:5 分鐘

設計叢集的目的

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

叢集配置方式

RabbitMQ可以通過三種方法來部署分散式叢集系統,分別是:cluster,federation,shovel

  • cluster:

    1. 不支援跨網段,用於同一個網段內的區域網
    2. 可以隨意的動態增加或者減少
    3. 節點之間需要執行相同版本的RabbitMQ和Erlang
  • federation:應用於廣域網,允許單臺伺服器上的交換機或佇列接收發布到另一臺伺服器上交換機或佇列的訊息,可以是單獨機器或叢集。federation佇列類似於單向點對點連線,訊息會在聯盟佇列之間轉發任意次,直到被消費者接受。通常使用federation來連線internet上的中間伺服器,用作訂閱分發訊息或工作佇列。

  • shovel:連線方式與federation的連線方式類似,但它工作在更低層次。可以應用於廣域網

RabbitMQ cluster 叢集同步原理

rabbitmq cluster
上面圖中採用三個節點組成了一個RabbitMQ的叢集,Exchange A的後設資料資訊在所有節點上是一致的,而Queue(存放訊息的佇列)的完整資料則只會存在於它所建立的那個節點上。,其他節點只知道這個queue的metadata資訊和一個指向queue的owner node的指標。

RabbitMQ叢集後設資料的同步

RabbitMQ叢集會始終同步四種型別的內部後設資料(類似索引):

  1. 佇列後設資料:佇列名稱和它的屬性;
  2. 交換器後設資料:交換器名稱、型別和屬性;
  3. 繫結後設資料:一張簡單的表格展示瞭如何將訊息路由到佇列;
  4. vhost後設資料:為vhost內的佇列、交換器和繫結提供名稱空間和安全屬性; 因此,當使用者訪問其中任何一個RabbitMQ節點時,通過rabbitmqctl查詢到的queue/user/exchange/vhost等資訊都是相同的。
為何RabbitMQ叢集僅採用後設資料同步的方式

一,儲存空間,如果每個叢集節點都擁有所有Queue的完全資料拷貝,那麼每個節點的儲存空間會非常大,叢集的訊息積壓能力會非常弱(無法通過叢集節點的擴容提高訊息積壓能力); 二,效能,訊息的釋出者需要將訊息複製到每一個叢集節點,對於持久化訊息,網路和磁碟同步複製的開銷都會明顯增加。

RabbitMQ cluster 叢集的兩種模式

  1. 普通模式:預設的叢集模式。
  2. 映象模式:把需要的佇列做成映象佇列,存在於多個節點,屬於RabbitMQ的HA方案

普通模式同步方式
普通模式:當訊息進入A節點的Queue中後,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行訊息傳輸,把A中的訊息實體取出並經過B傳送給consumer,所以consumer應平均連線每一個節點,從中取訊息。該模式存在一個問題就是當A節點故障後,B節點無法取到A節點中還未消費的訊息實體。如果做了佇列持久化或訊息持久化,那麼得等A節點恢復,然後才可被消費,並且在A節點恢復之前其它節點不能再建立A節點已經建立過的持久佇列;如果沒有持久化的話,訊息就會失丟。這種模式更適合非持久化佇列,只有該佇列是非持久的,客戶端才能重新連線到叢集裡的其他節點,並重新建立佇列。假如該佇列是持久化的,那麼唯一辦法是將故障節點恢復起來。

為什麼RabbitMQ不將佇列複製到叢集裡每個節點呢?這與它的叢集的設計本意相沖突,叢集的設計目的就是增加更多節點時,能線性的增加效能(CPU、記憶體)和容量(記憶體、磁碟)。當然RabbitMQ新版本叢集也支援佇列複製(有個選項可以配置)。比如在有五個節點的叢集裡,可以指定某個佇列的內容在2個節點上進行儲存,從而在效能與高可用性之間取得一個平衡(應該就是指映象模式)。

映象模式:其實質和普通模式不同之處在於,訊息實體會主動在映象節點間同步,而不是在consumer取資料時臨時拉取。該模式帶來的副作用也很明顯,除了降低系統效能外,如果映象佇列數量過多,加之大量的訊息進入,叢集內部的網路頻寬將會被這種同步通訊大大消耗掉。所以在對可靠性要求較高的場合中適用.

節點型別

  • RAM node:記憶體節點將所有的佇列、交換機、繫結、使用者、許可權和vhost的後設資料定義儲存在記憶體中,好處是可以使得像交換機和佇列宣告等操作更加的快速。

  • Disk node:將後設資料儲存在磁碟中,單節點系統只允許磁碟型別的節點,防止重啟RabbitMQ的時候,丟失系統的配置資訊。

如何檢視是何種節點
如果是記憶體結點這裡就顯示為RAM

注意

  • RabbitMQ要求在叢集中至少有一個磁碟節點,所有其他節點可以是記憶體節點,當節點加入或者離開叢集時,必須要將該變更通知到至少一個磁碟節點。
  • 如果叢集中唯一的一個磁碟節點崩潰的話,叢集仍然可以保持執行,但是無法進行其他操作(包括建立佇列、交換器、繫結,新增使用者、更改許可權、新增和刪除叢集結點),直到節點恢復。
  • 解決方案:設定兩個磁碟節點,至少有一個是可用的,可以儲存後設資料的更改。

Erlang Cookie

Erlang Cookie是保證不同節點可以相互通訊的金鑰,要保證叢集中的不同節點相互通訊必須共享相同的Erlang Cookie。具體的目錄存放在/var/lib/rabbitmq/.erlang.cookie

基本運維操作

用時:8 分鐘

rabbitmq叢集必要條件

繫結實體ip,即ifconfig所能查詢到的繫結到網路卡上的ip,以下是繫結方法

#編輯配置路徑 /etc/rabbitmq/rabbitmq-env.conf
NODE_IP_ADDRESS=172.16.136.133
複製程式碼

配置域名對映到實體ip

#配置檔案1所在路徑 /etc/rabbitmq/rabbitmq.config (如果是叢集,每臺機器都需要修改這個繫結本機實體ip)
#其中rabbit@master是建立叢集時所配置的引數,@後面的引數為主機名,示例中為master
[
    {rabbit, [
    {cluster_nodes, {['rabbit@master'], disc}},
    {cluster_partition_handling, ignore},
    {default_user, <<"guest">>},
    {default_pass, <<"guest">>},
    {tcp_listen_options, [binary,
        {packet, raw},
        {reuseaddr, true},
        {backlog, 128},
        {nodelay, true},
        {exit_on_close, false},
        {keepalive, true}]}
    ]},
    {kernel, [
        {inet_dist_listen_max, 44001},
        {inet_dist_listen_min, 44001}
    ]}
].
複製程式碼
#配置檔案2 所在路徑 /etc/hosts (如果是叢集,每臺機器都需要修改這個繫結本機實體ip,而且hosts檔案的對映不得重複,如果重複linux系統為以最下面一條記錄為準)
172.16.136.133 master
172.16.136.134 venus
172.16.136.135 venus2
複製程式碼

啟動停止

停止

#機器A
service rabbitmq-server stop
epmd -kill
#機器B
service rabbitmq-server stop
epmd -kill
#機器C
service rabbitmq-server stop
epmd -kill
複製程式碼

啟動

方式1

#機器A
service rabbitmq-server start
#機器B
service rabbitmq-server start
#機器C
service rabbitmq-server start
複製程式碼

方式2

rabbitmq-server -detached
複製程式碼

叢集重啟順序

叢集重啟的順序是固定的,並且是相反的。 如下所述:

啟動順序:磁碟節點 => 記憶體節點 關閉順序:記憶體節點 => 磁碟節點 最後關閉必須是磁碟節點,不然可能回造成叢集啟動失敗、資料丟失等異常情況。

重建叢集

注1:此處的mq叢集重建是比較快速和有效的方法,面向的是初次安裝或者可以接受mq中所存有的資料丟失的情況下,必須先有mq的.json字尾的配置檔案或者有把握寫入叢集中exchange、queue等配置。

按順序停止所有機器中的rabbitmq

#機器A
service rabbitmq-server stop
epmd -kill
#機器B
service rabbitmq-server stop
epmd -kill
#機器C
service rabbitmq-server stop
epmd -kill
複製程式碼

移除rabbitmq配置記錄與儲存檔案

#位於 /var/lib/rabbitmq/mensia
mv /var/lib/rabbitmq/mensia /var/lib/rabbitmq/mensia.bak
複製程式碼

按順序啟動所有機器中的rabbitmq

#機器C
service rabbitmq-server start
#機器B
service rabbitmq-server start
#機器A
service rabbitmq-server start
複製程式碼

停止被加入叢集節點app

比如A、B、C三臺機器,將B和C加入到A中去,需要執行以下命令

#機器B
rabbitmqctl stop_app
#機器C
rabbitmqctl stop_app
複製程式碼

建立叢集

注意此處master為唯一沒有執行rabbitmqctl stop_app的機器

#機器B
rabbitmqctl join_cluster rabbit@master
#機器C
rabbitmqctl join_cluster rabbit@master
複製程式碼

啟動叢集

#機器B
rabbitmqctl start_app
#機器C
rabbitmqctl start_app
複製程式碼

檢查叢集狀態

在任意一臺機器上執行rabbitmqctl cluster_status命令即可檢查,輸出包含叢集中的節點與執行中的節點,兼以主機名標誌

新增叢集配置

建立使用者

例子中建立了兩個使用者 新增使用者add_user,設定角色set_user_tags,新增rabbitmq虛擬主機add_vhost,設定訪問許可權set_permissions,以下是詳細用法

    # 建立第一個使用者
    /usr/sbin/rabbitmqctl add_user 使用者名稱 密碼
    /usr/sbin/rabbitmqctl set_user_tags 使用者名稱 administrator
    /usr/sbin/rabbitmqctl set_permissions -p /  使用者名稱 ".*" ".*" ".*"
    # 建立第二個使用者
    /usr/sbin/rabbitmqctl add_user 使用者名稱2 密碼
    /usr/sbin/rabbitmqctl set_user_tags 使用者名稱2 management 
    /usr/sbin/rabbitmqctl add_vhost sip_ext 
    /usr/sbin/rabbitmqctl set_permissions -p sip_ext 使用者名稱2 '.*' '.*' '.*' 
複製程式碼
備註:RabbitMQ 虛擬主機,RabbitMQ 通過虛擬主機(vhost)來分發訊息。擁有自己獨立的許可權控制,不同的vhost之間是隔離的,單獨的。

許可權控制的基本單位:vhost。

使用者只能訪問與之繫結的vhost。

vhost是AMQP中唯一無法通過協議來建立的基元。只能通過rabbitmqctl工具來建立。 
複製程式碼
開啟15672網頁管理端,訪問mq

/usr/sbin/rabbitmq-plugins enable rabbitmq_management 備註:如果發現命令執行完畢沒有開啟此服務,15672埠沒有監聽,則是由於沒有重啟mq導致的

在底部匯入.json字尾的配置檔案即可

http://localhost:4000/first-blog/rabbitmq.jpg

rabbitmq 原理、叢集、基本運維操作、常見故障處理

如果覆蓋了使用者需要使用以下命令修改mq使用者密碼 /usr/sbin/rabbitmqctl change_password 使用者名稱 密碼

修改節點型別

rabbitmqctl stop_app

rabbitmqctl change_cluster_node_type dist

rabbitmqctl change_cluster_node_type ram

rabbitmqctl start_app
複製程式碼

常用命令

常用命令

常見故障

用時:3 分鐘

  • 叢集狀態異常
    1. rabbitmqctl cluster_status檢查叢集健康狀態,不正常節點重新加入叢集
    2. 分析是否節點掛掉,手動啟動節點。
    3. 保證網路連通正常
  • 佇列阻塞、資料堆積
    1. 保證網路連通正常
    2. 保證消費者正常消費,消費速度大於生產速度
    3. 保證伺服器TCP連線限制合理
  • 腦裂
    1. 按正確順序重啟叢集
    2. 保證網路連通正常
    3. 保證磁碟空間、cpu、記憶體足夠

引用

RabbitMQ簡介、通道(channel)

RabbitMQ基礎概念詳細介紹

RabbitMQ的幾種典型使用場景 AMQP介紹

RabbitMQ分散式叢集架構

RabbitMQ系列(六)你不知道的RabbitMQ叢集架構全解

訊息中介軟體—RabbitMQ(叢集原理與搭建篇)

相關文章