天天看點

淺談消息中間件之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架構?

淺談消息中間件之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程序模型?

淺談消息中間件之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(基礎篇)

在使用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:直連交換器

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

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

路由鍵與隊列名完全比對,如果一個隊列綁定到交換機要求路由鍵為“dog”,則隻轉發 routing key 标記為“dog”的消息,

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

fanout exchange:扇形交換器

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

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

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

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

topic exchange(主題交換器):

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

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

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

第2步:拉取鏡像檔案

docker pull rabbitmq  (最新版本)

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

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

第3步:建立并啟動容器

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

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

第4步:頁面通路驗證啟動成功

預設的賬戶登入,使用者名和密碼都guest

直接通路主界面通路不了,是因為沒有安裝rabbitmq_management插件,進入容器 ,安裝即可

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

安裝插件:rabbitmq-plugins enable rabbitmq_management

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

docker搭建rabbitmq叢集

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 [email protected]

rabbitmqctl start_app

exit

節點3(預設磁盤節點)加入節點1叢集

docker exec -it rabbitmqCluster03 bash

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl join_cluster  [email protected]

rabbitmqctl start_app

exit

      節點4(記憶體節點 --ram)加入節點1叢集

docker exec -it rabbitmqCluster04 /bin/bash

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl join_cluster --ram [email protected]

rabbitmqctl start_app

exit

節點5(預設磁盤節點)加入節點1叢集

docker exec -it rabbitmqCluster05 bash

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl join_cluster  [email protected]

rabbitmqctl start_app

exit

叢集搭建完成

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

Caption