rabbitmq 原理、集羣、基本運維操做、常見故障處理

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

摘要

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

用時:25 分鐘linux

原理與概念

用時:9 分鐘json

簡介

AMQP,即Advanced Message Queuing Protocol,高級消息隊列協議,是應用層協議的一個開放標準,爲面向消息的中間件設計。消息中間件主要用於組件之間的解耦,消息的發送者無需知道消息使用者的存在,反之亦然。安全

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

解決的問題

RabbitMQ就是當前最主流的消息中間件之一。服務器

  • 兩個(多個)系統間須要經過定時任務來同步某些數據
  • 異構系統的不一樣進程間相互調用、通信的問題

Queue

Queue(隊列)是RabbitMQ的內部對象,用於存儲消息,用下圖表示。 cookie

RabbitMQ中的消息都只能存儲在Queue中,生產者(下圖中的P)生產消息並最終投遞到Queue中,消費者(下圖中的C)能夠從Queue中獲取消息並消費。

多個消費者能夠訂閱同一個Queue,這時Queue中的消息會被平均分攤給多個消費者進行處理,而不是每一個消費者都收到全部的消息並處理。 網絡

技術術語

  • 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聯合使用才能最終生效。
    在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
下面是消息發送的過程

  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

若是覆蓋了用戶須要使用如下命令修改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(集羣原理與搭建篇)

相關文章
相關標籤/搜索