談談RabbitMQ

消息通信

常見的web服務之間的通信機制有兩種 ,同步和異步。
同步方法有RMI、Hessin、Burlap、HTTP invoker,雖然同步通信比較簡單,但是存在如下問題:服務需要等待,耦合度高!而異步通信就不存在這些問題,它無需等待,web服務只要將消息發送後就可以馬上繼續執行;對象對象和解耦;位置獨立,消息發起者只需要知道 消息服務器的位置就可以發送消息,消息接收也無需知道發起者的具體位置,它只需要知道消息服務器在哪裏就能獲取消息。
常見的異步消中間件有kafaka、RabbitMQ、ZeroMQ、ActiveMQ,他們之間各有優勢,該用哪一種需要看實際需求而定。本文只介紹RabbitMQ的一些知識。

AMQP協議

介紹RabbitMQ就必須先介紹AMQP協議,因爲RabbitMQ是它的一種實現而已。

AMQP,即Advanced Message Queuing Protocol,一個提供統一消息服務的應用層標準高級消息隊列協議,是應用層協議的一個開放標準,爲面向消息的中間件設計。基於此協議的客戶端與消息中間件可傳遞消息,並不受客戶端/中間件不同產品,不同的開發語言等條件的限制。

AMQP模型

這裏寫圖片描述

  1. Server(broker): 接受客戶端連接,實現AMQP消息隊列和路由功能的進程,可以理解爲郵局。

  2. Virtual Host:其實是一個虛擬概念,類似於權限控制組,一個Virtual Host裏面可以有若干個Exchange和Queue,當多個不同的用戶使用同一個RabbitMQ server提供的服務時,可以劃分出多個vhost,每個用戶在自己的vhost創建exchange/queue等,就好比於tomcat中webapps目錄下可以部署多個web項目。

3.Exchange:接受生產者發送的消息,並根據Binding規則將消息路由給服務器中的隊列,就好比郵遞員。

4.Message Queue:消息隊列,用於存儲還未被消費者消費的消息,就好比於郵箱。

5.Message: 由Header和Body組成,Header是由生產者添加的各種屬性的集合,包括Message是否被持久化、由哪個Message Queue接受、優先級是多少等,就好比於郵箱裏面的信件。而Body是真正需要傳輸的APP數據,就像信件裏面的信紙。

6.Binding:Binding聯繫了Exchange與Message Queue。Exchange在與多個Message Queue發生Binding後會生成一張路由表,路由表中存儲着Message Queue所需消息的限制條件即Binding Key。當Exchange收到Message時會解析其Header得到Routing Key,Exchange根據Routing Key與Exchange Type將Message路由到Message Queue。Binding Key由Consumer在Binding Exchange與Message Queue時指定,而Routing Key由Producer發送Message時指定,兩者的匹配方式由Exchange Type決定,就好比於郵件上面的地址。

7.Connection:連接,對於RabbitMQ而言,其實就是一個位於客戶端和Broker之間的TCP連接。

8.Channel:信道,僅僅創建了客戶端到Broker之間的連接後,客戶端還是不能發送消息的。需要爲每一個Connection創建Channel,AMQP協議規定只有通過Channel才能執行AMQP的命令。一個Connection可以包含多個Channel。之所以需要Channel,是因爲TCP連接的建立和釋放都是十分昂貴的,如果一個客戶端每一個線程都需要與Broker交互,如果每一個線程都建立一個TCP連接,暫且不考慮TCP連接是否浪費,就算操作系統也無法承受每秒建立如此多的TCP連接,可以簡單的理解爲線程池中的一個個線程。

RabbitMQ模型

上文說到RabbitMQ只是AMQP的一種實現,只不過是使用了ErLang來實現的,因此RabbitMQ的模型和AMQP模型也就大同小異了。

這裏寫圖片描述

理解了AMQP的模型,對於RabbitMQ模型也就理解了。

這裏的RabbiMQ Server就是broker,RoutingKey也就是Binding的意思,對於Exchange,RabbitMQ總共有4種不同的常用類型(比JMS多了整整一倍啊!),fanout、direct、topic、headers。前三種不做解釋了,簡單的總結一下前三種:fanout——往每家每戶都發送郵件;direct——往某一戶人家發送郵件;topic——往姓張的家裏發送郵件。詳細的可以看一下我寫的前一篇RabbitMq文章

headers type:上文提到了一個Message由headers和body組成,body代表消息實體,headers則代表了消息的各種屬性。因此headers類型的Exchange是根據發送的消息內容中的headers屬性進行匹配。

消息確認(Confirm)機制

RabbitMQ的消息確認機制是爲了確保消息發送者知道自己發佈的消息被正確接收,如果沒有收到確認時就會認爲消息發送過程發送了錯誤,此時就會馬上採取措施,以保證消息能正確送達(類似於HTTP的建立連接時的確認答覆)。
具體做法如下:
當RabbitMQ發送消息以後,如果收到消息確認,纔將該消息從Quque中移除。如果RabbitMQ沒有收到確認,如果檢測到消費者的RabbitMQ鏈接斷開,則RabbitMQ 會將該消息發送給其他消費者;否則就會重新再次發送一個消息給消費者。

持久化

RabbitMQ的一大特性就是支持消息持久化。但是Rabbit MQ默認是不持久隊列、Exchange、Binding以及隊列中的消息的,這意味着一旦消息服務器重啓,所有已聲明的隊列,Exchange,Binding以及隊列中的消息都會丟失,這是因爲支持持久化會對性能造成較大的影響。

什麼時候需要持久化?

1.我們根據自己的需求對它們進行持久化(具體方法可以參考官方的API)。

注意:消息是存在隊列裏的,如果要使得消息能持久化,就必須先使隊列持久化。

2.內存緊張時,需要將部分內存中的消息轉移到磁盤中。

消息如何刷到磁盤?

1.寫入文件前會有一個Buffer,大小爲1M,數據在寫入文件時,首先會寫入到這個Buffer,如果Buffer已滿,則會將Buffer寫入到文件(未必刷到磁盤)。
2.有個固定的刷盤時間:25ms,也就是不管Buffer滿不滿,每個25ms,Buffer裏的數據及未刷新到磁盤的文件內容必定會刷到磁盤。
3.每次消息寫入後,如果沒有後續寫入請求,則會直接將已寫入的消息刷到磁盤:使用Erlang的receive x after 0實現,只要進程的信箱裏沒有消息,則產生一個timeout消息,而timeout會觸發刷盤操作。

RPC

RabbitMQ中也支持RPC,具體實現如下:

1.客戶端發送請求(消息)時,在消息的屬性中設置兩個值replyTo(用於告訴服務器處理完成後將通知我的消息發送到這個Queue中)和correlationId(此次請求的標識號,服務器處理完成後需要將此屬性返還,客戶端將根據這個id瞭解哪條請求被成功執行了或執行失敗)

2.服務器端收到消息並處理

3.服務器端處理完消息後,將生成一條應答消息到replyTo指定的Queue,同時帶上correlationId屬性

4.客戶端之前已訂閱replyTo指定的Queue,從中收到服務器的應答消息後,根據其中的correlationId屬性分析哪條請求被執行了,根據執行結果進行後續業務處理

這裏寫圖片描述

老闆讓祕書去買東西,告訴祕書將買到的東西送到他家門口保衛處,並寫上自己的名字。
他家門口保衛處——replyTo。
自己的名字——correlationId。

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