RabbitMQ下的生產消費者模式與訂閱發佈模式

  所謂模式,就是在某種場景下,一類問題及其解決方案的總結歸納。生產消費者模式與訂閱發佈模式是使用消息中間件時常用的兩種模式,用於功能解耦和分佈式系統間的消息通信,以下面兩種場景爲例:

  • 數據接入
      假設有一個用戶行爲採集系統,負責從App端採集用戶點擊行爲數據。通常會將數據上報和數據處理分離開,即App端通過REST API上報數據,後端拿到數據後放入隊列中就立刻返回,而數據處理則另外使用Worker從隊列中取出數據來做,如下圖所示。


      這樣做的好處有:第一,功能分離,上報的API接口不關心數據處理功能,只負責接入數據;第二,數據緩衝,數據上報的速率是不可控的,取決於用戶使用頻率,採用該模式可以一定程度地緩衝數據;第三,易於擴展,在數據量大時,通過增加數據處理Worker來擴展,提高處理速率。這便是典型的生產消費者模式,數據上報爲生產者,數據處理爲消費者。

  • 事件分發
      假設有一個電商系統,那麼,用戶“收藏”、“下單”、“付款”等行爲都是非常重要的事件,通常後端服務在完成相應的功能處理外,還需要在這些事件點上做很多其他處理動作,比如發送短信通知、記錄用戶積分等等。我們可以將這些額外的處理動作放到每個模塊中,但這並不是優雅的實現,不利於功能解耦和代碼維護。
      我們需要的是一個事件分發系統,在各個功能模塊中將對應的事件發佈出來,由對其感興趣的處理者進行處理。這裏涉及兩個角色:A對B感興趣,A是處理者,B是事件,由事件處理器完成二者的綁定,並向消息中心訂閱事件。服務模塊是後端的業務邏輯服務,在不同的事件點發布事件,事件經過消息中心分發給事件處理器對應的處理者。整個流程如下圖所示。這邊是典型的訂閱發佈模式


  可以看到,生產消費者模式與訂閱發佈模式都離不開消息中間件來作爲消息中轉站,開源的消息中間件有很多,各有優劣。本文將重點探討RabbitMQ的特性,以及如何實現上述的兩種場景。

RabbitMQ核心概念

  如果你只是想使用一下RabbitMQ,那麼參考官方教程修改一下就可以跑起來了,很簡單。如果你有一些概念上的疑惑,不妨與筆者一起來總結一下RabbitMQ的核心概念。

  • 通信方式
      RabbitMQ是基於AMQP協議來實現的消息中間件。AMQP,類似於HTTP協議,也是一個應用層的協議,網絡層使用TCP來通信。因此,RabbitMQ也是典型的C-S模型,準確地說是C-S-C模型,因爲伴隨着RabbitMQ的使用,總是會有Producer與Consumer兩個Client和一個Broker Server。


      Client要與Server進行通信,就必須先建立連接,RabbitMQ中有Connection與Channel兩個概念,前者就是一個TCP連接,後者是在這個連接上的虛擬概念,負責邏輯上的數據傳遞,因此,爲了節省資源,一般在一個客戶端中建立一個Connection,每次使用時再分配一個Channel即可。

  • 消息體
      Message是RabbitMQ中的消息體概念。類似HTTP傳輸中,有header和body兩部分數據,Message中也有Attributes和Payload兩部分數據,前者是一些元信息,後者是傳遞的消息數據實體。

  • 消息投遞
      Exchange、Queue與Routing Key三個概念是理解RabbitMQ消息投遞的關鍵。RabbitMQ中一個核心的原則是,消息不能直接投遞到Queue中。Producer只能將自己的消息投遞到Exchange中,由Exchange按照routing_key投遞到對應的Queue中,具體的架構參見下圖。細細品味就會體會到這樣設計的精妙之處。


      那麼,具體實現時,如何完成這三者關係的綁定?總結起來是兩點:第一,在Consumer Worker中,聲明自己對哪個Exchange感興趣,並將自己的Queue綁定到自己感興趣的一組routing_key上,建立相應的映射關係;第二,在Producer中,將消息投遞一個Exchange中,並指明它的routing_key。由此可見,Queue這個概念只是對Consumer可見,Producer並不關心消息被投遞到哪個Queue中。
      看過RabbitMQ的”Hello World”教程的童鞋可能會發現在那裏面的圖中並沒有看到Exchange和routing_key的蹤跡,但這並不意味着RabbitMQ可以支持直接將消息投遞到Queue中,而是在內部使用了默認的Exchange和routing_key了。默認情況下,RabbitMQ使用名稱爲“amq.direct”的Direct Exchange,routing_key默認名字與Queue保持一致。
      搞清楚上述概念,就不難理解Exchange的四種類型了。Direct、Fanout、Topic、Headers,區別在於如何將消息從Exchange投遞到Queue中。Direct使用具體的routing_key來投遞;Fanout則忽略routing_key,直接廣播給所有的Queue;Topic是使用模糊匹配來對一組routing_key進行投遞;Headers也是忽略routing_key,使用消息中的Headers信息來投遞。

  • 消息可靠性
      不同於HTTP的同步訪問,RabbitMQ中,Producer並不知道消息是否被可靠地投遞到了Consumer中處理。那麼,RabbitMQ是如何保證消息的可靠投遞?主要是兩點:第一,消息確認機制。Consumer處理完消息後,需要發送確認消息給Broker Server,可以選擇“確認接收”、“丟棄”、“重新投遞”三種方式。如果Consumer在Broker Server收到確認消息之前掛了,Broker Server便會重新投遞該消息。第二,可以選擇數據持久化,這樣即使RabbitMQ重啓,也不會丟失消息。

生產消費者模式

  搞清楚了RabbitMQ的核心概念,要針對特定的場景來設計使用方案就很簡單了,基本上就是上述RabbitMQ架構圖的變遷。讓我們先來看看文章開頭提到的“數據接入”場景,如何實現生產消費者模式。



  這裏增加了一下場景複雜度:對於上報的數據,如果是special的行爲,需要優先處理。從上圖可以看到,數據上報端負責將數據投遞到RabbitMQ對應的Exchange,並指明routing_key是common還是special。數據處理端,可以根據情況啓多個Woker來消費數據,但至少需要兩個,一個用來處理common數據,一個用來處理special的數據。注意:當需要增加多個Worker來消費同一類數據時,需要保持Queue名字一致,比如上面的Common數據。

訂閱發佈模式

  再來看“事件分發”的場景,架構如下圖所示,使用event name/id來作爲RabbitMQ的routing key的名字。Event Processor 01對event 01 和event 02感興趣,則在啓動Consumer Worker時,將自己的Queue綁定到這兩個routing key上即可,其他Event Processor也是如此,這樣便完成了事件的訂閱。當有事件發佈時,消息便會按照event name/id被投遞到對應的Queue中。



  由此可見,在不同的應用中,變化的只是routing_key與Consumer Queue的綁定關係,在充分理解RabbitMQ的核心概念後處理這些應該也是得心應手了。



(全文完,本文地址:http://blog.csdn.net/zwgdft/article/details/53561277
Bruce,2016/12/11


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