《RabbitMQ實戰指南》整理(六)跨越集羣與高階應用

RabbitMQ可以通過三種方式進行分佈式部署:集羣、Federation、Shovel,Federation和Shove提供了更高的靈活性,但也提高了部署的複雜性

一、Federation

Federation插件設計的目的是使RabbitMQ在不同的Broker節點之間進行消息傳遞而無需建立集羣,其功能如下:

  • 在不同管理域(不同的用戶、vhost、或不同版本的RabbitMQ和Erlang)中的Broker或者集羣之間傳遞消息;
  • 基於AMQP協議在不同的Broker之間進行通信,並且能夠容忍不穩定的網絡連接情況;
  • 一個Broker節點可以同時存在聯邦交換器(或隊列)或者本地交換器(或隊列),需對指定特定交換器或隊列創建Frderation連接(聯邦交換器允許一個本地消費者接受來自上游隊列的消息);
  • 不需要在多個Broker之間創建多個連接

1、聯邦交換器

假設broker1和broker2中的exchangeA的交互存在一定的延遲,則可利用Federation在broker1上建立一個同名的exchangeA,並在內部建立交換器exchangeA->broker2 B,並通過路由鍵將這兩個交換器綁定起來,與此同時會在內部建立一個federation:exchangeA->broker B隊列。Federation會在federation:exchangeA->broker B隊列和exchangeA之間建立一個AMQP連接來實施的消費隊列federation:exchangeA->broker B中的數據,其扮演了一個內部交換器的角色。

聯邦交換器可以成爲另一個交換器的上游交換器,兩方交換器也可以互爲聯邦交換器和上游交換器。需要注意的是不能對默認和交換器和內部交換器使用Federation功能

2、聯邦隊列

聯邦隊列可以在多個broker節點或集羣之間爲單個隊列提供負載均衡的功能,一個聯邦交換器可以連接一個或多個上游隊列,並從這些上游隊列中獲取消息以滿足本地消費者消費消息的需求

假設brokerA和brokerB,brokerB中的隊列queue1和queue2需要將brokerA作爲上游,使用Federation後會在brokerA中建立同名的隊列queue1和queue2,後續brokerB會將拉取brokerA中相應隊列的消息,當brokerB中的queue1或2有消息堆積時,則不會繼續拉取上游brokerA的消息消費,即消費者可以既消費brokerA中消息又可以消息brokerB中的消息,當一端來不及消費時另一方可以幫忙消費,以此達到某種意義上的負載均衡。

兩個隊列可以互爲聯邦隊列,一個消息可以在兩個聯邦隊列間轉發無限次。需要注意的是聯邦隊列只能使用Basic.Consume進行消費。此外聯邦隊列不具有傳遞性

3、Federation的使用

使用Federation需要配置下述內容:

  • 配置一個或多個upstream,每個upstream均定義其它節點的Federation link
  • 定義匹配交換器或者隊列的一種/多種策略

執行rabbitmq-plugins enable rabbitmq_federation可以開啓Federation功能,它會默認開啓amqp_client插件,另外如果要開啓Federation插件需要執行rabbitmq-plugins enable rabbitmq_feseration_management命令(該插件需要依附rabbitmq_management插件)

有關Federation uostream的信息全部保存在RabbitMQ的Mnesia數據庫中,包括用戶信息,權限信息,隊列信息等。Federation中存在3種級別的配置:①upstreams爲每個upstream定義與其它Broker建立連接的消息;②Upstream sets用於對一系列使用Federation功能的upstream進行分組;③Policies:每一個Policy選定一組交換器或隊列,或對兩者進行限定進而作用於一個單獨upstream或者upstream set之上。其中upstream set可以忽略替換爲all

upstream和upstream sets都屬於運行時參數,每個vhost都持有不同的參數和策略集合。Federation相關的運行時參數和策略可以通過rabbitmqctl工具、HTTP API接口或rabbitmq_federation_management插件提供的Web管理界面配置

二、Shovel

與Federation的數據轉發功能類似,Shovel能夠可靠、持續地從一個Broker種的隊列(源端)拉取數據並轉發至另一個Broker種的交換器(目的端),源端的隊列和目的端的隊列可以位於同一個Broker或不同Broker。其優勢在於:

  • 松耦合:可以移動位於不同管理域中的Broker上的消息;
  • 支持廣域網:基於AMQP協議在Broker之間進行通信,可以容忍時斷時續的連通,保證消息的可靠性;
  • 高度定製:Shovel成功連接後,可以對其配置以執行相關的AMQP命令

1、Shovel的原理

通常情況下,使用Shovel時配置隊列作爲源端,交換器作爲目的端。當配置隊列作爲目的端時,中間也是經過交換器轉發,而這個交換器是默認交換器。當配置交換器作爲源端時,broker會新建一個隊列並綁定這個交換器,最後從這個隊列中拉取消息進行轉發。

涉及到Shovel的broker、exchange和queue可以在Shovel link建立之前創建,也可以在Shovel成功連接源端或目的端的Broker之後創建。Shovel可以爲源端或目的端配置多個Broker的地址,使得源端或目的端的Broker失效後能夠重新連接到其它Broker上(隨機選擇)。可以設置reconnect_delay參數避免重連行爲導致的網絡泛洪,或者可以在重連失敗後停止連接。

2、Shovel的使用

Shovel插件默認在RabbitMQ發佈包中,執行rabbitmq-plugins ebable rabbitmq_shovel命令可以開啓Shovel功能,如果要開啓Shovel管理插件,需要執行rabbitmq-plugins enable rabbitmq_shovel_management命令。Shovel可以部署在源端或者目的端,另外有兩種方式可以部署Shovel:靜態方式和動態方式。靜態方式是指在config文件中進行設置,動態方式是指通過Runtime Parameter設置

靜態方式:config文件中針對Shovel插件的配置信息由單條Shovel條目構成,每一條Shovel條目定義了源端和目的端的轉發關係

動態方式:與Federation upstream類似,Shovel動態部署方式的配置信息會被保存到RabbitMQ的Mnesia數據庫中,包括權限信息,用戶信息和隊列信息。每一個ShovelLink由一個相應的Parameter定義,它可以通過rabbitmqctl工具,RabbitMQ Management管理界面或者HTTP API接口方式進行添加。

3、通過Shovel解決消息堆積

當某個隊列中的消息堆積嚴重超過某個閾值時,可以通過Shovel將隊列中的消息移交給另一個集羣

三、存儲機制

1、持久化的消息在到達隊列時就被寫到磁盤,如果可以會在內存中保存一份備份以提高性能,當內存喫緊時會從內存中清除;非持久化消息一般只保存在內存中,內存喫緊時會保存到磁盤中以節省內存空間;兩種消息類型都是在RabbitMQ的持久層中完成。

2、持久層是邏輯上的概念,實際包含兩個部分,隊列索引rabbit_queue_index和消息存儲rabbit_msg_store:

  • 隊列索引負責維護隊列中落盤消息的信息,包括消息的存儲地點,是否已被交付等,是否已被消費等,每個隊列都有一個隊列索引
  • 消息存儲以鍵值對的形式存儲消息,它被所有隊列共享,在每個節點中有且只有一個;具體可以分爲msg_store_persisten和msg_store_transient,msg_store_persisten負責持久化消息的持久,msg_store_transient負責非持久化消息的持久

3、消息(包括消息體、屬性和headers)可以存儲在隊列索引中,也可以存儲在消息存儲中,較佳的配置是較小的消息存儲在隊列索引中,較大的消息存儲在消息存儲中,消息大小可以通過queue_index_embed_msgs_below來配置,默認大小爲4096B

rabbit_queue_index中以順序的段文件進行存儲,每個段文件中包含固定的SEGMENT_ENTRY_Count條記錄,默認值爲16384。每個rabbit_queue_index從磁盤中讀取消息時至少要在內存中維護一個段文件,所以設置queue_index_embed_msgs_below值要謹慎

4、經過rabbit_msg_store處理的所有消息都會以追加的方式寫入文件中,當一個文件大小超過指定的限制時(file_size_limit)時,會關閉這個文件再創建一個新的文件供消息寫入,文件名會從0開始累加。

在消息存儲時,RabbitMQ會記錄消息在文件中的位置映射和文件的相關消息,在讀取消息時,會根據消息的ID找到對應存儲的文件,文件存在且未被鎖住則直接打開文件從指定位置讀取文件,如果文件不存在或被鎖住則發送請求由rabbit_msg_store處理。消息的刪除只是從Rabbit記錄的表中刪除指定消息的相關信息,並更新對應存儲文件的相關信息,並不會立即對文件中的消息進行刪除,而僅僅是做一個標誌,當檢測到前後兩個文件的有效數據可以合併時,並且垃圾數據的大小和所有文件(至少3個文件)的數據大小比值超過設定的閾值時(默認0.5)會觸發垃圾回收將兩個文件進行合併,合併的文件一定是邏輯上相鄰的兩個文件

1、隊列的結構

1、隊列通常由rabbitmq_amqqueue_process和backing_queue兩部分組成,rabbitmq_amqqueue_process負責協議相關消息的處理,包括接受生產者發佈的消息、向消費者交付消息、處理消息的確認。backing_queue是消息存儲的具體形式和引擎,並向rabbitmq_amqqueue_process提供相關的接口以供調用

2、消息存入隊列後其狀態會發生變化,會存在下面4種狀態:

  • alpha:消息內容和消息索引存儲在內存中;
  • beta:消息內容存儲在磁盤中,消息索引存儲在內存中;
  • gamma:消息內容存儲在磁盤中,消息索引在磁盤和內存中都有;
  • delta:消息內容和索引都在磁盤中

對於持久化消息,消息內容和消息索引都必須先保存在磁盤上,才能處於上述狀態的一種,gamma消息只有持久化消息纔會有。RabbitMQ會在運行時根據消息傳送速度定期計算一個當前內存中能夠保存的最大消息數量,當alpha狀態的消息數據大於這個值時,會引起消息狀態的轉換,多餘的消息會轉換到其它狀態。alpha狀態最耗內存,delta需要讀消息索引和讀消息內容進行兩次IO操作,消耗較多的CPU和磁盤IO,beta和gamma只需要一次磁盤IO操作就可以從消息存儲中讀取到消息

3、backing_queue默認實現是rabbit_variable_queue,其內部通過5個子隊列Q1、Q2、Delat、Q3和Q4來體現消息的各個狀態。Q1和Q4只包含alpha狀態的消息,Q2和Q3包含beta和gamma狀態的消息,Delat只包含delta狀態的消息,一般情況下消息按照Q1->Q2->Delta->Q3->Q4順序進行流動。消費者獲取消息也會引起消息狀態的轉換

2、惰性隊列

惰性隊列會盡可能的將消息存入磁盤中,而在消費者消費到相應的消息時纔會被加載到內存中,其設計目標是爲了支持更長的隊列支持更多的消息存儲。當消費者由於各種原因導致消息堆積時,惰性隊列就會很有用。在聲明隊列的時候可以通過x-queue-mode參數來設置隊列的模式,取值爲default和lazy

四、內存及磁盤告警

當內存使用超過配置的閾值或磁盤空間低於配置的閾值時,RabbitMQ會暫時阻塞客戶端的連接並停止接受從客戶端發來的消息,同時心跳檢測也會失效。爲阻止生產者的同時又不影響消費者的運行,建議將生產者和消費者的邏輯分攤到獨立的Connection上。

1、內存告警

RbbitMQ服務器會在啓動或執行rabbitmqctl set_vm_memory_high_watermark fraction命令時計算計算機內存的大小,默認情況下vm_memory_high_watermark的值爲0.4,即當RabbitMQ使用的內存超過內存的40%時,就會產生內存警告並阻塞生產者的連接。

內存閾值可以通過config文件進行配置或者通過命令rabbitmqctl set_vm_memory_high_watermark fraction命令進行設置,fraction的值即爲閾值。正常情況下建議該值在0.4~0.66之間。通過命令設置的閾值會在重啓後失效,而config文件設定的閾值則不會失效,但是需要重啓才能生效

當某個broker節點觸及到內存並阻塞生產者之前它會嘗試將隊列中的消息換頁到磁盤以釋放內存空間,默認情況下內存到達閾值的50%時就會進行換頁動作,可以在config文件中修改vm_memory_high_watermark_paging_ration項來修改該值

2、磁盤告警

當剩餘磁盤空間低於確定的閾值時,RabbitMQ同樣會阻塞生產者以避免因非持久化消息持續換頁而耗盡磁盤空間導致服務崩潰。默認情況下,磁盤閾值爲50MB。正常情況下RabbitMQ會每10秒一次進行磁盤剩餘空間的檢查,當要到達磁盤閾值時,檢測頻率爲每秒10次。

可以通過config文件中的disk_free_limit來設置磁盤閾值,或者將磁盤閾值設置爲集羣內存的比值,一般建議取值在1.0~2.0之間

五、流控

流控機制是用來避免消息的發送速率過快而導致服務器難以支撐的情形,內存和磁盤告警相當於全局的流控,而此處的流控是針對的單個Connection

1、流控的原理

Erlang進程之間並不共享內存,而是通過消息傳遞來通信,每個進程都有自己的進程郵箱,默認情況下Erlang不對進程郵箱的大小進行限制,所有當有大量消息持續發往某個進程時,會導致該進程郵箱過大。RabbitMQ中使用基於信用證算法的流控機制來限制發送消息的速率來解決這個問題,它通過監控各個進程的進程郵箱,當某個進行負載過高時就會開始堆積消息,當堆積到一定的量之後則會開始阻塞不接受消息,進而上游進程也會因堆積消息而停止接受上游的消息,最後負責網絡數據包接受的進程會阻塞而暫停接受新的數據

一個Connection觸發流控時會處於flow狀態,這意味着這個Connection的狀態每秒在blocked和unblocked之間來回切換多次,進而將消息發送的速率控制在服務器能夠支撐的範圍內。流控狀態不僅作用於Connection,同樣還作用於信道和隊列,從Connection到Channel,再到隊列,最後是消息持久化存儲,形成了一個完成的流程鏈,只要某個進程阻塞,其上游進程必定阻塞

2、打破性能瓶頸

提高隊列的性能一般有兩種解決方案,第一種是開啓Erlang語言的HiPE功能,能保守提高30%~40%的性能,另一種是尋求打破rabbitmq_amqqueue_process的性能瓶頸,這裏是指以多個rabbitmq_amqqueue_process替換單個rabbitmq_amqqueue_process,這樣可以充分利用上Connection或者Channel進程中被流控的性能

六、鏡像隊列

引入鏡像隊列可以將隊列鏡像到集權中的其它broker節點之上,當集羣中的一個節點失效了,隊列能夠自動地切換到鏡像中的另一個節點上以保證服務的可用性。通常用法下,一個配置鏡像的隊列都包含一個主節點和若干個從節點,從節點會按照主節點執行命令的順序進行動作。如果master由於某種原因失效,那麼加入時間最早的salve會被提升爲新的master

如果消費者與slave建立連接並消費,本質上都是從master上獲取消息。鏡像隊列同時支持publish confirm和事務兩種機制,在事務中只有在全部鏡像中執行之後,客戶端纔會收到OK消息,publish confirm中同理

鏡像隊列的配置是通過添加相應的Policy來完成的。默認情況下,鏡像隊列中的消息不會主動同步到新的slave中,除非顯示調用同步命令,所以由於同步過程的限制,不建議對生產環境中正在使用的隊列進行操作。但所有slave都出現未同步狀態,如果master由於主動原因停掉,那麼slave不會接管master,而如果是系統崩潰等被動原因停掉則會接管,我們可以將ha-promote-on-shutdown設置爲always以保證可用性。

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