淺談消息中間件之RabbitMQ(基礎篇)

 

什麼是rabbitMQ?

rabbitMQ有什麼特點?

rabbitMQ能夠做什麼?

rabbitMQ架構?

rabbitMQ進程模型?

rabbitMQ爲什麼要使用信通channel?

Exchange(交換機)與交換機的類型?

Queue(隊列)屬性有哪些?

消息確認是什麼?拒絕消息是什麼?消息預約是什麼?

消息有哪些屬性?

dockrabbitMQ的搭建(單機版)

 

docker搭建rabbitmq集羣?

docker下單機多節點集羣搭建?


什麼是rabbitMQ?

rabbitMQ是一款開源的,Erlang編寫的,基於AMQP協議的消息中間件;

AMQP協議 :Advanced Message Queue,高級消息隊列協議。AMQP(高級消息隊列協議)是一個網絡協議。它支持符合要求的客戶端應用(application)和消息中間件代理(messaging middleware broker)之間進行通信。

擴展AMQP協議分解參考:https://blog.csdn.net/vipshop_fin_dev/article/details/81612935

rabbitMQ有什麼特點?

  • 可靠性:RabbitMQ提供了多種技術可以讓你在性能和可靠性之間進行權衡。這些技術包括持久性機制、投遞確認、發佈者證實和高可用性機制。
  • 靈活性:消息在到達隊列前是通過交換機進行路由的,對於典型的路由功能,RabbitMQ 已經提供了一些內置的 Exchange 來實現;針對更復   雜的路由功能,可以將多個 Exchange 綁定在一起,也通過插件機制實現自己的 Exchange。

  • 消息集羣(Clustering)
    多個 RabbitMQ 服務器可以組成一個集羣,形成一個邏輯 Broker 。

  • 高可用(Highly Available Queues)
    隊列可以在集羣中的機器上進行鏡像,使得在部分節點出問題的情況下隊列仍然可用。

  • 多種協議(Multi-protocol)
    RabbitMQ 支持多種消息隊列協議,比如 STOMP、MQTT 等等。

  • 多語言客戶端(Many Clients)
    RabbitMQ 幾乎支持所有常用語言,比如 Java、.NET、Ruby 等等。

  • 管理界面(Management UI)
    RabbitMQ 提供了一個易用的用戶界面,使得用戶可以監控和管理消息 Broker 的許多方面。

  • 跟蹤機制(Tracing)
    如果消息異常,RabbitMQ 提供了消息跟蹤機制,使用者可以找出發生了什麼。

  • 插件機制(Plugin System)
    RabbitMQ 提供了許多插件,來從多方面進行擴展,也可以編寫自己的插件。

  • 活躍的社區技術支持

圍繞着RabbitMQ有一個大型的社區,那兒有着各種各樣的客戶端、插件、指南等等。

rabbitMQ能夠做什麼?

消息系統允許軟件、應用相互連接和擴展.這些應用可以相互鏈接起來組成一個更大的應用,或者將用戶設備和數據進行連接.消息系統通過將消息的發送和接收分離來實現應用程序的異步和解偶。

通俗點:它可以爲你的應用提供一個通用的消息發送和接收平臺,並且保證消息在傳輸過程中的安全。

rabbitMQ架構?

 

術語說明:

publish(生成者):發佈消息的服務實體。

Exchange(交換機):用來發送消息的AMQP實體,它指定消息按什麼路由規則,路由到哪個隊列。

Routing Key(路由關鍵字) :交換機(Exchange)根據這個關鍵字進行消息投遞、綁定。

Binding(綁定):將交換機和隊列(Queue)按照一定的路由規則綁定起來。

Queue(隊列):每個消息都會被投入到一個或多個隊列。

Broker(消息代理):一個消息代理,這裏可以指RabbitMQ。

VHost(虛擬主機):虛擬主機,一個消息代理(Broker)裏可以開設多個虛擬主機(vhost),用作不同用戶的權限分離。

Consumer(消費者):消費消息的服務實體。每個消費者(訂閱者)都有一個叫做消費者標籤的標識符。

它可以被用來退訂消 息。消費者標籤實際上是一個字符串。

Channel(通道):AMQP通過通道(channels)來處理多連接,可以把通道理解成共享一個TCP連接的多個輕量化連接。

Connection(連接):AMQP連接通常是長連接,Producer和Consumer都是通過TCP連接到RabbitMQ Server的。

rabbitMQ進程模型?

事件驅動模型(或者說反應堆模型),這是一種高性能的非阻塞io線程模型,不過在Erlang中稱爲進程模型。

tcp_acceptor進程接收客戶端連接,創建rabbit_reader、rabbit_writer、rabbit_channel進程。

rabbit_reader接收客戶端連接,解析AMQP幀;rabbit_writer向客戶端返回數據;

rabbit_channel解析AMQP方法,對消息進行路由,然後發給相應隊列進程。

rabbit_amqqueue_process是隊列進程,在RabbitMQ啓動(恢復durable類型隊列)或創建隊列時創建。

rabbit_msg_store是負責消息持久化的進程。

在整個系統中,存在一個tcp_accepter進程,一個rabbit_msg_store進程,有多少個隊列就有多少個rabbit_amqqueue_process進程,每個客戶端連接對應一個rabbit_reader和rabbit_writer進程。
 

rabbitMQ爲什麼要使用信通channel?

在使用rabbitmq時不管是消費還是生產都需要創建信道(channel) 和connection(連接),如下圖producer示例。

我們完全可以直接使用Connection就能完成信道的工作,爲什麼還要引入信道呢,試想這樣一個場景,一個應用有多個線程

需要從rabbitmq中消費,或是生產消息,那麼必然會建立很多個connection ,也就是多個tcp連接,對操作系統而言,

建立和銷燬tcp連接是很昂貴的開銷,如果遇到使用高峯,性能瓶頸也隨之顯現,rabbitmq採用類似nio的做法,

連接tcp連接複用,不僅可以減少性能開銷,同時也便於管理。

每個線程都把持一個信道,所以信道複用了TCP連接。同時rabbitmq可以確保每個線程的私密性,就像擁有獨立的連接一樣。

當每個信道的流量不是很大時,複用單一的connection可以再產生性能瓶頸的情況下有效地節省tcp連接資源,

但是當信道本身的流量很大時,這時候多個信道複用一個connection就會產生性能瓶頸,進而是整體的流量被限制了。

此時就需要開闢多個connection,將這些信道均攤到這些connection中,至於這些相關調優策略需要根據業務自身的

實際情況進行調節。

Exchange(交換機)與交換機的類型?

參考:https://www.jianshu.com/p/79ca08116d57/  

           http://rabbitmq.mr-ping.com/AMQP/AMQP_0-9-1_Model_Explained.html

交換機類型外,在聲明交換機時還可以附帶許多其他的屬性,其中最重要的幾個分別是:

  • Name
  • Durability (消息代理重啓後,交換機是否還存在)
  • Auto-delete (當所有與之綁定的消息隊列都完成了對此交換機的使用後,刪掉它)
  • Arguments(依賴代理本身)

交換機可以有兩個狀態:持久(durable)、暫存(transient)。持久化的交換機會在消息代理(broker)重啓後依舊存在,

而暫存的交換機則不會(它們需要在代理再次上線後重新被聲明)。然而並不是所有的應用場景都需要持久化的交換機。

默認交換機(Default)

默認交換機(default exchange)不是一個真正的交換機類型,實際上是一個由消息代理(Broker)預先聲明好的沒有名字

(名字爲空字符串)的直連交換機。它有一個特殊的屬性:那就是每個新建隊列(queue)都會自動綁定到默認交換機上,

綁定的路由鍵(routing key)名稱與隊列名稱相同。對於一些不復雜的場景都會使用這一特殊屬性。
 

direct exchange:直連交換器

消息中的路由鍵(routing key)如果和 Binding 中的 binding key 一致, 交換器就將消息發到對應的隊列中。

路由鍵與隊列名完全匹配,如果一個隊列綁定到交換機要求路由鍵爲“dog”,則只轉發 routing key 標記爲“dog”的消息,

不會轉發“dog.puppy”,也不會轉發“dog.guard”等等。它是完全匹配、單播的模式。

fanout exchange:扇形交換器

每個發到 fanout 類型交換器的消息都會分到所有綁定的隊列上去。fanout 交換器不處理路由鍵,

只是簡單的將隊列綁定到交換器上,每個發送到交換器的消息都會被轉發到與該交換器綁定的所有隊列上。

很像子網廣播,每臺子網內的主機都獲得了一份複製的消息。fanout 類型轉發消息是最快的。

topic exchange(主題交換器):

topic 交換器通過模式匹配分配消息的路由鍵屬性,將路由鍵和某個模式進行匹配,此時隊列需要綁定到一個模式上。

它將路由鍵和綁定鍵的字符串切分成單詞,這些單詞之間用點隔開。它同樣也會識別兩個通配符:符號“#”和符號“”。

#匹配0個或多個單詞,匹配不多不少一個單詞。

header exchange(頭交換器):

頭交換機可以視爲直連交換機的另一種表現形式。頭交換機能夠像直連交換機一樣工作,不同之處在於頭交換機的路由規則

是建立在頭屬性值之上,而不是路由鍵。路由鍵必須是一個字符串,而頭屬性值則沒有這個約束,它們甚至可以是整數或者

哈希值(字典)等。

 

Queue(隊列)屬性有哪些?

隊列:它們存儲着即將被應用消費掉的消息。隊列跟交換機共享某些屬性,但是隊列也有一些另外的屬性。

  • Name:隊列的名字可以由應用(application)來取,也可以讓消息代理(broker)直接生成一個。隊列的名字可以是最多255字節的一個utf-8字符串。

  • Durable(消息代理重啓後,隊列依舊存在):持久化隊列(Durable queues)會被存儲在磁盤上,當消息代理(broker)重啓的時候,它依舊存在。沒有被持久化的隊列稱作暫存隊列(Transient queues)。

  • Exclusive(只被一個連接(connection)使用,而且當連接關閉後隊列即被刪除)

  • Auto-delete(當最後一個消費者退訂後即被刪除)

  • Arguments(一些消息代理用他來完成類似與TTL的某些額外功能)

隊列在聲明(declare)後才能被使用。如果一個隊列尚不存在,聲明一個隊列會創建它。如果聲明的隊列已經存在,

並且屬性完全相同,那麼此次聲明不會對原有隊列產生任何影響。如果聲明中的屬性與已存在隊列的屬性有差異,

那麼一個錯誤代碼爲406的通道級異常就會被拋出。

消息確認是什麼?拒絕消息是什麼?消息預約是什麼?

消息確認:爲了確保資源空間,消息代理會刪除隊列裏的消息或隊列以避免資源的浪費,怎麼確保消息代理刪除的消息是

消費者已經消費呢?

消息代理給我們提供了2種建議:

  • 自動確認模式:當消息代理(AMQP)將消息發送給應用後立即刪除。(使用AMQP方法:basic.deliver或basic.get-ok)
  • 顯示確認模式:待消費者(cumster)發送一個確認回執(acknowledgement)後再刪除消息。(使用AMQP方法:basic.ack)。

如果一個消費者在尚未發送確認回執的情況下掛掉了,那AMQP代理會將消息重新投遞給另一個消費者。

如果當時沒有可用的消費者了,消息代理會死等下一個註冊到此隊列的消費者,然後再次嘗試投遞。

舉例:我們在京東或者某寶購物,我們收到快遞了可以在頁面上去操作確認收貨(顯示確認模式)已告知商家已經收貨,

如果我們忘記操作或者一般人都不去操作這個,這時候商家怎麼確認用戶已經收貨了呢?

jd、某寶平臺有個默認7天自動收貨(自動確認模式)。

還是這個購物例子,如果我們覺得買的商品有問題(運輸途中損壞、商家發錯貨了)當然也可以拒收,這個時候商家收到拒收的

消息他就重新發貨給用戶,(這個時候用戶需要把消息寄回商家)

拒絕消息:同理、當消息代理模式接收到消息拒絕這一指令時,消費者可以告訴消息代理如何處理這條消息——銷燬它或者

重新放入隊列。

當此隊列只有一個消費者時,請確認不要由於拒絕消息並且選擇了重新放入隊列的行爲而引起消息在同一個消費者身上無限

循環的情況發生。

在AMQP中,basic.reject方法用來執行拒絕消息的操作。但basic.reject有個限制:你不能使用它決絕多個帶有確認回執的消息。

但是如果你使用的是RabbitMQ,那麼你可以使用被稱作negative acknowledgements(也叫nacks)的AMQP 0-9-1擴展來解決

這個問題。更多的信息請參考幫助頁面

消息預約:在多個消費者共享一個隊列的案例中,明確指定在收到下一個確認回執前每個消費者一次可以接受多少條消息是

非常有用的。這可以在試圖批量發佈消息的時候起到簡單的負載均衡和提高消息吞吐量的作用。

舉例:我們去銀行辦理業務,我們要先去機子上打印一個預約號碼,我們在大廳等着叫號,當廣播到自己的號碼再去對應的窗口

辦理業務,這樣就避免了所有人都堆在一起都想先辦,結果櫃檯一天下來辦理不到幾個。消息預約同樣的道理。

消息有哪些屬性?

AMQP模型中的消息(Message)對象是帶有屬性(Attributes)的。

  • Content type(內容類型)
  • Content encoding(內容編碼)
  • Routing key(路由鍵)
  • Delivery mode (persistent or not)
    投遞模式(持久化 或 非持久化)
  • Message priority(消息優先權)
  • Message publishing timestamp(消息發佈的時間戳)
  • Expiration period(消息有效期)
  • Publisher application id(發佈應用的ID)

有些屬性是被AMQP代理所使用的,但是大多數是開放給接收它們的應用解釋器用的。有些屬性是可選的也被稱作

消息頭(headers)。他們跟HTTP協議的X-Headers很相似。消息屬性需要在消息被髮布的時候定義。

消息能夠以持久化的方式發佈,AMQP代理會將此消息存儲在磁盤上。如果服務器重啓,系統會確認收到的持久化消息未丟失。

dockrabbitMQ的搭建(單機版)

由於時間關係,暫時介紹docker版本下安裝rabbitMQ(MAC版),這裏不做都的介紹,

有興趣的可以參看windows、liunx環境下安裝

不熟悉docker的可以先去熟系下docker

第1步:查看需要下載的鏡像版本

https://hub.docker.com/_/rabbitmq?tab=tags

 

第2步:拉取鏡像文件

docker pull rabbitmq  (最新版本)

--docker pull rabbitmq:3.8   (下載3.8版本)

 

第3步:創建並啓動容器

docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq

第4步:頁面訪問驗證啓動成功

默認的賬戶登錄,用戶名和密碼都guest

直接訪問主界面訪問不了,是因爲沒有安裝rabbitmq_management插件,進入容器 ,安裝即可

進入容器:docker exec -it rabbitmq /bin/bash

安裝插件:rabbitmq-plugins enable rabbitmq_management

 

docker搭建rabbitmq集羣

rabbitmq集羣怎麼實現不同節點之間消息同步的呢?

RabbitMQ集羣僅採用元數據同步的方式:

RabbitMQ天然支持Clustering。這使得RabbitMQ本身不需要像ActiveMQ、Kafka那樣通過ZooKeeper分別來實現HA方案和保存集羣的元數據,所以我們不必去在載zookeeper。

RabbitMQ集羣元數據的同步

RabbitMQ 集羣中的所有節點都會備份所有的元數據信息, 包括以下內容。

  • 隊列元數據:隊列的名稱及屬性;

  • 令交換器:交換器的名稱及屬性:

  • 綁定關係元數據:交換器與隊列或者交換器與交換器之間的綁定關係;

  • vhost 元數據:爲 vhost 內的隊列、交換器和綁定提供命名空間及安全屬性。

爲何RabbitMQ集羣僅採用元數據同步的方式

第一,存儲空間,如果每個集羣節點都擁有所有Queue的完全數據拷貝,那麼每個節點的存儲空間會非常大,

集羣的消息積壓能力會非常弱(無法通過集羣節點的擴容提高消息積壓能力);

第二,性能,消息的發佈者需要將消息複製到每一個集羣節點,對於持久化消息,網絡和磁盤同步複製的開銷都會明顯增加。

採取同步元數據可能導致原因?

因爲節點直接同步原數據而不保存消息,所有當集羣中某一個 RabbitMQ 節點崩潰時,該節點上的所有隊列中的消息也會丟失,

有沒有好的方式解決由於節點丟失而導致消息丟失問題呢?

配置鏡像隊列(後續詳解)

集羣模式,普通集羣和鏡像集羣區別?

第一種 普通集羣模式:rabbitmq集羣與其他集羣有些不同,rabbitmq集羣同步的指是複製隊列,元數據信息的同步,

即同步的是數據存儲信息;消息的存放只會存儲在創建該消息隊列的那個節點上。並非在節點上都存儲一個完整的數據。

在通過非數據所在節點獲取數據時,通過元數據信息,路由轉發到存儲數據節點上,從而得到數據 。

第二種 鏡像集羣模式:與普通集羣模式區別 主要是消息實體會主動在鏡像節點間同步數據,而不是隻存儲數據元信息。

故普通集羣模式 但凡數據節點掛了,容易造成數據丟失但鏡像集羣模式可以保證集羣只要不全部掛掉,數據就不會丟失,

當相對於性能來說,鏡像集羣模式會比普通集羣模式多出消耗數據的傳輸。故取決於業務場景進行取捨。

docker下單機多節點集羣搭建?

2磁盤+2鏡像節點

         第1步:創建文件夾

        mkdir  rabbitmqcluster

cd rabbitmqcluster

mkdir  rabbitmq01 rabbitmq02 rabbitmq03 rabbitmq04 rabbitmq05
         第2步:先創建5個節點容器

docker run -d --hostname rabbitmq01 --name rabbitmqCluster01 -v /Users/home/rabbitmqcluster/rabbitmq01:/var/lib/rabbitmq -p 15672:15672 -p 5672:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' rabbitmq

docker run -d --hostname rabbitmq02 --name rabbitmqCluster02 -v /Users/home/rabbitmqcluster/rabbitmq02:/var/lib/rabbitmq -p 15673:15672 -p 5673:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie'  --link rabbitmqCluster01:rabbitmq01 rabbitmq

docker run -d --hostname rabbitmq03 --name rabbitmqCluster03 -v /Users/home/rabbitmqcluster/rabbitmq03:/var/lib/rabbitmq -p 15674:15672 -p 5674:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie'  --link rabbitmqCluster01:rabbitmq01 --link rabbitmqCluster02:rabbitmq02  rabbitmq

docker run -d --hostname rabbitmq04 --name rabbitmqCluster04 -v /Users/home/rabbitmqcluster/rabbitmq04:/var/lib/rabbitmq -p 15675:15672 -p 5675:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie'  --link rabbitmqCluster01:rabbitmq01 --link rabbitmqCluster02:rabbitmq02  --link rabbitmqCluster03:rabbitmq03  rabbitmq

docker run -d --hostname rabbitmq05 --name rabbitmqCluster05 -v /Users/home/rabbitmqcluster/rabbitmq05:/var/lib/rabbitmq -p 15676:15672 -p 5676:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie'  --link rabbitmqCluster01:rabbitmq01 --link rabbitmqCluster02:rabbitmq02 --link rabbitmqCluster03:rabbitmq03 --link rabbitmqCluster04:rabbitmq04  rabbitmq

 

第3步:加入節點集羣

節點1(磁盤節點),重啓啓動

docker exec -it rabbitmqCluster01 /bin/bash

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl start_app

exit

節點2(內存節點 --ram)加入節點1集羣

docker exec -it rabbitmqCluster02 /bin/bash

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl join_cluster --ram rabbit@rabbitmq01

rabbitmqctl start_app

exit

節點3(默認磁盤節點)加入節點1集羣

docker exec -it rabbitmqCluster03 bash

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl join_cluster  rabbit@rabbitmq01

rabbitmqctl start_app

exit

      節點4(內存節點 --ram)加入節點1集羣

docker exec -it rabbitmqCluster04 /bin/bash

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl join_cluster --ram rabbit@rabbitmq01

rabbitmqctl start_app

exit

節點5(默認磁盤節點)加入節點1集羣

docker exec -it rabbitmqCluster05 bash

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl join_cluster  rabbit@rabbitmq01

rabbitmqctl start_app

exit

集羣搭建完成

Caption

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章