rabbitmq原理介紹(轉載)

從AMQP協議能夠看出,MessageQueue、Exchange和Binding構成了AMQP協議的核心,下面咱們就圍繞這三個主要組件    從應用使用的角度全面的介紹如何利用Rabbit MQ構建消息隊列以及使用過程當中的注意事項。正則表達式

 

RabbitMQ系列二(構建消息隊列) - 網易杭研後臺技術中心 - 網易杭研後臺技術中心的博客

 

 

  • 1. 聲明MessageQueue安全

      在Rabbit MQ中,不管是生產者發送消息仍是消費者接受消息,都首先須要聲明一個MessageQueue。這就存在一個問題,是生產者聲明仍是消費者聲明呢?要解決這個問題,首先須要明確:服務器

a)消費者是沒法訂閱或者獲取不存在的MessageQueue中信息。異步

b)消息被Exchange接受之後,若是沒有匹配的Queue,則會被丟棄。函數

在明白了上述兩點之後,就容易理解若是是消費者去聲明Queue,就有可能會出如今聲明Queue以前,生產者已發送的消息被丟棄的隱患。若是應用可以經過消息重發的機制容許消息丟失,則使用此方案沒有任何問題。可是若是不能接受該方案,這就須要不管是生產者仍是消費者,在發送或者接受消息前,都須要去嘗試創建消息隊列。這裏有一點須要明確,若是客戶端嘗試創建一個已經存在的消息隊列,Rabbit MQ不會作任何事情,並返回客戶端創建成功的。性能

       若是一個消費者在一個信道中正在監聽某一個隊列的消息,Rabbit MQ是不容許該消費者在同一個channel去聲明其餘隊列的。Rabbit MQ中,能夠經過queue.declare命令聲明一個隊列,能夠設置該隊列如下屬性:spa

a) Exclusive:排他隊列,若是一個隊列被聲明爲排他隊列,該隊列僅對首次聲明它的鏈接可見,並在鏈接斷開時自動刪除。這裏須要注意三點:其一,排他隊列是基於鏈接可見的,同一鏈接的不一樣信道是能夠同時訪問同一個鏈接建立的排他隊列的。其二,「首次」,若是一個鏈接已經聲明瞭一個排他隊列,其餘鏈接是不容許創建同名的排他隊列的,這個與普通隊列不一樣。其三,即便該隊列是持久化的,一旦鏈接關閉或者客戶端退出,該排他隊列都會被自動刪除的。這種隊列適用於只限於一個客戶端發送讀取消息的應用場景。.net

b)   Auto-delete:自動刪除,若是該隊列沒有任何訂閱的消費者的話,該隊列會被自動刪除。這種隊列適用於臨時隊列。隊列

 c)   Durable:持久化,這個會在後面做爲專門一個章節討論。事務

d)  其餘選項,例如若是用戶僅僅想查詢某一個隊列是否已存在,若是不存在,不想創建該隊列,仍然能夠調用queue.declare,只不過須要將參數passive設爲true,傳給queue.declare,若是該隊列已存在,則會返回true;若是不存在,則會返回Error,可是不會建立新的隊列。

  • 2. 生產者發送消息

        在AMQP模型中,Exchange是接受生產者消息並將消息路由到消息隊列的關鍵組件。ExchangeType和Binding決定了消息的路由規則。因此生產者想要發送消息,首先必需要聲明一個Exchange和該Exchange對應的Binding。能夠經過 ExchangeDeclare和BindingDeclare完成。在Rabbit MQ中,聲明一個Exchange須要三個參數:ExchangeName,ExchangeType和Durable。ExchangeName是該Exchange的名字,該屬性在建立Binding和生產者經過publish推送消息時須要指定。ExchangeType,指Exchange的類型,在RabbitMQ中,有三種類型的Exchange:direct ,fanout和topic,不一樣的Exchange會表現出不一樣路由行爲。Durable是該Exchange的持久化屬性,這個會在消息持久化章節討論。聲明一個Binding須要提供一個QueueName,ExchangeName和BindingKey。下面咱們就分析一下不一樣的ExchangeType表現出的不一樣路由規則。

        生產者在發送消息時,都須要指定一個RoutingKey和Exchange,Exchange在接到該RoutingKey之後,會判斷該ExchangeType:

                         a) 若是是Direct類型,則會將消息中的RoutingKey與該Exchange關聯的全部Binding中的BindingKey進行比較,若是相等,則發送到該Binding對應的Queue中。

RabbitMQ系列二(構建消息隊列) - 網易杭研後臺技術中心 - 網易杭研後臺技術中心的博客 

                  b)   若是是  Fanout  類型,則會將消息發送給全部與該  Exchange  定義過  Binding  的全部  Queues  中去,實際上是一種廣播行爲。
           

RabbitMQ系列二(構建消息隊列) - 網易杭研後臺技術中心 - 網易杭研後臺技術中心的博客 

        c)若是是Topic類型,則會按照正則表達式,對RoutingKey與BindingKey進行匹配,若是匹配成功,則發送到對應的Queue中。

             RabbitMQ系列二(構建消息隊列) - 網易杭研後臺技術中心 - 網易杭研後臺技術中心的博客

  • 3. 消費者訂閱消息    

    在RabbitMQ中消費者有2種方式獲取隊列中的消息:

       a)  一種是經過basic.consume命令,訂閱某一個隊列中的消息,channel會自動在處理完上一條消息以後,接收下一條消息。(同一個channel消息處理是串行的)。除非關閉channel或者取消訂閱,不然客戶端將會一直接收隊列的消息。

             b)  另一種方式是經過basic.get命令主動獲取隊列中的消息,可是絕對不能夠經過循環調用basic.get來代替basic.consume,這是由於basic.get RabbitMQ在實際執行的時候,是首先consume某一個隊列,而後檢索第一條消息,而後再取消訂閱。若是是高吞吐率的消費者,最好仍是建議使用basic.consume。

      若是有多個消費者同時訂閱同一個隊列的話,RabbitMQ是採用循環的方式分發消息的,每一條消息只能被一個訂閱者接收。例如,有隊列Queue,其中ClientA和ClientB都Consume了該隊列,MessageA到達隊列後,被分派到ClientA,ClientA回覆服務器收到響應,服務器刪除MessageA;再有一條消息MessageB抵達隊列,服務器根據「循環推送」原則,將消息會發給ClientB,而後收到ClientB的確認後,刪除MessageB;等到再下一條消息時,服務器會再將消息發送給ClientA。

       這裏咱們能夠看出,消費者再接到消息之後,都須要給服務器發送一條確認命令,這個便可以在handleDelivery裏顯示的調用basic.ack實現,也能夠在Consume某個隊列的時候,設置autoACK屬性爲true實現。這個ACK僅僅是通知服務器能夠安全的刪除該消息,而不是通知生產者,與RPC不一樣。 若是消費者在接到消息之後還沒來得及返回ACK就斷開了鏈接,消息服務器會重傳該消息給下一個訂閱者,若是沒有訂閱者就會存儲該消息。

        既然RabbitMQ提供了ACK某一個消息的命令,固然也提供了Reject某一個消息的命令。當客戶端發生錯誤,調用basic.reject命令拒絕某一個消息時,能夠設置一個requeue的屬性,若是爲true,則消息服務器會重傳該消息給下一個訂閱者;若是爲false,則會直接刪除該消息。固然,也能夠經過ack,讓消息服務器直接刪除該消息而且不會重傳。

  • 4. 持久化:

        Rabbit MQ默認是不持久隊列、Exchange、Binding以及隊列中的消息的,這意味着一旦消息服務器重啓,全部已聲明的隊列,Exchange,Binding以及隊列中的消息都會丟失。經過設置Exchange和MessageQueue的durable屬性爲true,可使得隊列和Exchange持久化,可是這還不能使得隊列中的消息持久化,這須要生產者在發送消息的時候,將delivery mode設置爲2,只有這3個所有設置完成後,才能保證服務器重啓不會對現有的隊列形成影響。這裏須要注意的是,只有durable爲true的Exchange和durable爲ture的Queues才能綁定,不然在綁定時,RabbitMQ都會拋錯的。持久化會對RabbitMQ的性能形成比較大的影響,可能會降低10倍不止。

  • 5. 事務:

     對事務的支持是AMQP協議的一個重要特性。假設當生產者將一個持久化消息發送給服務器時,由於consume命令自己沒有任何Response返回,因此即便服務器崩潰,沒有持久化該消息,生產者也沒法獲知該消息已經丟失。若是此時使用事務,即經過txSelect()開啓一個事務,而後發送消息給服務器,而後經過txCommit()提交該事務,便可以保證,若是txCommit()提交了,則該消息必定會持久化,若是txCommit()還未提交即服務器崩潰,則該消息不會服務器就收。固然Rabbit MQ也提供了txRollback()命令用於回滾某一個事務。

  • 6. Confirm機制:

      使用事務當然能夠保證只有提交的事務,纔會被服務器執行。可是這樣同時也將客戶端與消息服務器同步起來,這背離了消息隊列解耦的本質。Rabbit MQ提供了一個更加輕量級的機制來保證生產者能夠感知服務器消息是否已被路由到正確的隊列中——Confirm。若是設置channel爲confirm狀態,則經過該channel發送的消息都會被分配一個惟一的ID,而後一旦該消息被正確的路由到匹配的隊列中後,服務器會返回給生產者一個Confirm,該Confirm包含該消息的ID,這樣生產者就會知道該消息已被正確分發。對於持久化消息,只有該消息被持久化後,纔會返回Confirm。Confirm機制的最大優勢在於異步,生產者在發送消息之後,便可繼續執行其餘任務。而服務器返回Confirm後,會觸發生產者的回調函數,生產者在回調函數中處理Confirm信息。若是消息服務器發生異常,致使該消息丟失,會返回給生產者一個nack,表示消息已經丟失,這樣生產者就能夠經過重發消息,保證消息不丟失。Confirm機制在性能上要比事務優越不少。可是Confirm機制,沒法進行回滾,就是一旦服務器崩潰,生產者沒法獲得Confirm信息,生產者其實自己也不知道該消息吃否已經被持久化,只有繼續重發來保證消息不丟失,可是若是原先已經持久化的消息,並不會被回滾,這樣隊列中就會存在兩條相同的消息,系統須要支持去重。

 

  • 其餘:

Broker:簡單來講就是消息隊列服務器實體。
Exchange:消息交換機,它指定消息按什麼規則,路由到哪一個隊列。
Queue:消息隊列載體,每一個消息都會被投入到一個或多個隊列。
Binding:綁定,它的做用就是把exchange和queue按照路由規則綁定起來。
Routing Key:路由關鍵字,exchange根據這個關鍵字進行消息投遞。
vhost:虛擬主機,一個broker裏能夠開設多個vhost,用做不一樣用戶的權限分離。
producer:消息生產者,就是投遞消息的程序。
consumer:消息消費者,就是接受消息的程序。
channel:消息通道,在客戶端的每一個鏈接裏,可創建多個channel,每一個channel表明一個會話任務。

消息隊列的使用過程大概以下:

(1)客戶端鏈接到消息隊列服務器,打開一個channel。 (2)客戶端聲明一個exchange,並設置相關屬性。 (3)客戶端聲明一個queue,並設置相關屬性。 (4)客戶端使用routing key,在exchange和queue之間創建好綁定關係。 (5)客戶端投遞消息到exchange。

相關文章
相關標籤/搜索