RabbitMQ 隊列分爲幾種類型,按照不一樣維度來分,能夠分爲排他性隊列、普通隊列、延遲隊列、惰性隊列、發佈訂閱隊列等。redis
今天咱們討論的主角是惰性隊列 Lazy Queue。衆所周知,隊列能夠存儲消息並實現消息收發,這應該是消息隊列中最重要的功能之一。算法
咱們使用消息隊列有幾個優點,解耦、高效、發完無論、高可用。上一篇咱們聊了RabbitMQ的鏡像隊列機制,鏡像隊列是高可用實現的一個有利保障,但在高可用的同時,必須提供高效的服務,才能被更多普通勞苦大衆所接受。mongodb
RabbitMQ提供高效服務的幾種途徑,設置RAM節點、自動同步,先內存後磁盤的讀取方式。在隊列設置持久化後,咱們讀取隊列中的消息,若是都是先從內存後從磁盤讀取,那麼無形會佔用更多系統資源,畢竟內存應該留給更多有須要的地方。緩存
若是發送端過快或消費端宕機,致使消息大量積壓,此時消息仍是在內存和磁盤各存儲一份,在消息大爆發的時候,MQ服務器會撐不住,影響其餘隊列的消息收發,能不能有效的處理這種狀況呢。答案 惰性隊列。服務器
RabbitMQ從3.6.0版本開始引入了惰性隊列(Lazy Queue)的概念。惰性隊列會盡量的將消息存入磁盤中,而在消費者消費到相應的消息時纔會被加載到內存中,它的一個重要的設計目標是可以支持更長的隊列,即支持更多的消息存儲。當消費者因爲各類各樣的緣由(好比消費者下線、宕機亦或者是因爲維護而關閉等)而導致長時間內不能消費消息形成堆積時,惰性隊列就頗有必要了併發
默認狀況下,當生產者將消息發送到RabbitMQ的時候,隊列中的消息會盡量的存儲在內存之中,這樣能夠更加快速的將消息發送給消費者。即便是持久化的消息,在被寫入磁盤的同時也會在內存中駐留一份備份。當RabbitMQ須要釋放內存的時候,會將內存中的消息換頁至磁盤中,這個操做會耗費較長的時間,也會阻塞隊列的操做,進而沒法接收新的消息。雖然RabbitMQ的開發者們一直在升級相關的算法,可是效果始終不太理想,尤爲是在消息量特別大的時候app
惰性隊列會將接收到的消息直接存入文件系統中,而不論是持久化的或者是非持久化的,這樣能夠減小了內存的消耗,可是會增長I/O的使用,若是消息是持久化的,那麼這樣的I/O操做不可避免,惰性隊列和持久化消息可謂是「最佳拍檔」。注意若是惰性隊列中存儲的是非持久化的消息,內存的使用率會一直很穩定,可是重啓以後消息同樣會丟失
高併發
隊列具有兩種模式:default和lazy。默認的爲default模式,在3.6.0以前的版本無需作任何變動。lazy模式即爲惰性隊列的模式,能夠經過調用channel.queueDeclare方法的時候在參數中設置,也能夠經過Policy的方式設置,若是一個隊列同時使用這兩種方式設置的話,那麼Policy的方式具有更高的優先級。若是要經過聲明的方式改變已有隊列的模式的話,那麼只能先刪除隊列,而後再從新聲明一個新的
設置隊列爲惰性隊列的命令:性能
rabbitmqctl set_policy Lazy "^myqueue$" '{"queue-mode":"lazy"}' --apply-to queues
惰性隊列和普通隊列相比,只有很小的內存開銷。這裏很難對每種狀況給出一個具體的數值,可是咱們能夠類比一下:當發送1千萬條消息,每條消息的大小爲1KB,而且此時沒有任何的消費者,那麼普通隊列會消耗1.2GB的內存,而惰性隊列只消耗1.5MB的內存測試
據官網測試數據顯示,對於普通隊列,若是要發送1千萬條消息,須要耗費801秒,平均發送速度約爲13000條/秒。若是使用惰性隊列,那麼發送一樣多的消息時,耗時是421秒,平均發送速度約爲24000條/秒。出現性能誤差的緣由是普通隊列會因爲內存不足而不得不將消息換頁至磁盤。若是有消費者消費時,惰性隊列會耗費將近40MB的空間來發送消息,對於一個消費者的狀況,平均的消費速度約爲14000條/秒。
若是要將普通隊列轉變爲惰性隊列,那麼咱們須要忍受一樣的性能損耗。當轉變爲惰性隊列的時候,首先須要將緩存中的消息換頁至磁盤中,而後才能接收新的消息。反之,當將一個惰性隊列轉變爲普通隊列的時候,和恢復一個隊列執行一樣的操做,會將磁盤中的消息批量的導入到內存中。
隊列參數的設置:
Message TTL(x-message-ttl):設置隊列中的全部消息的生存週期(統一爲整個隊列的全部消息設置生命週期), 也能夠在發佈消息的時候單獨爲某個消息指定剩餘生存時間,單位毫秒, 相似於redis中的ttl,生存時間到了,消息會被從隊裏中刪除,注意是消息被刪除,而不是隊列被刪除, 特性Features=TTL, 單獨爲某條消息設置過時時間AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties().builder().expiration(「6000」);
Auto Expire(x-expires): 當隊列在指定的時間沒有被訪問(consume, basicGet, queueDeclare…)就會被刪除,Features=Exp
Max Length(x-max-length): 限定隊列的消息的最大值長度,超過指定長度將會把最先的幾條刪除掉, 相似於mongodb中的固定集合,例如保存最新的100條消息, Feature=Lim
Max Length Bytes(x-max-length-bytes): 限定隊列最大佔用的空間大小, 通常受限於內存、磁盤的大小, Features=Lim B
Dead letter exchange(x-dead-letter-exchange): 當隊列消息長度大於最大長度、或者過時的等,將從隊列中刪除的消息推送到指定的交換機中去而不是丟棄掉,Features=DLX
Dead letter routing key(x-dead-letter-routing-key):將刪除的消息推送到指定交換機的指定路由鍵的隊列中去, Feature=DLK
Maximum priority(x-max-priority):優先級隊列,聲明隊列時先定義最大優先級值(定義最大值通常不要太大),在發佈消息的時候指定該消息的優先級, 優先級更高(數值更大的)的消息先被消費,
Lazy mode(x-queue-mode=lazy): Lazy Queues: 先將消息保存到磁盤上,不放在內存中,當消費者開始消費的時候才加載到內存中
Master locator(x-queue-master-locator)
總結:
惰性隊列的存在是爲了減小內存佔用,避免因爲內存不足而產生的換頁操做。
但對比惰性隊列和普通隊列,若是在內存和磁盤均不設限制的話,採用普通隊列的效率會更高,畢竟磁盤再快,對比內存也會差一個數量級。
但若是服務器的配置捉襟見肘的話,能夠考慮惰性隊列的存在,畢竟惰性隊列相比普通隊列,性能差別並不大,在極端狀況下還會更好,您以爲呢?
使用lazy queue會有如下幾種搭配
lazy queue 消息不持久化 , 可是這種模式仍是會把消息放到硬盤裏,RAM的使用率會一直很穩定,可是重啓後同樣會丟失消息
lazy queue 消息持久化,這種方式無疑是最佳搭配,消息放到硬盤而且不會由於服務器重啓而丟失,面對高併發也是從容不已。 麪包和咖啡更配哦