記一次RabbitMQ連接阻塞,全部隊列不消費異常

image

前幾天博主遇到一個很狗屎的bug,RabbitMQ本來運行的好好突然所有的消息隊列都不消費了,看了一下 Connections連接,發現全部都發生阻塞了,導致線上的隊列堆積如山,情況萬分危急。

推測一:生產者和消費者問題

剛開始推測是不是生產者和消費者出問題了,然後就檢查了一下服務的運行狀態,發現都沒問題,說明不是這個問題引起的,所以進一步猜測可能是MQ本身出現問題了。

推測二:MQ本身出現問題

如果是MQ出現問題,那MQ的日誌肯定會有錯誤的相關信息記載,所以我們進入MQ日誌下面,查看日誌情況。

/var /log/rabbitmq/

日誌結果如下所示:

=ERROR REPORT==== 30-May-2019::19:28:49 ===
connection <0.23331.7132>, channel 4 - soft error:
{amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}

=INFO REPORT==== 30-May-2019::19:28:49 ===
vm_memory_high_watermark clear. Memory used:383597048 allowed:385616281

=WARNING REPORT==== 30-May-2019::19:28:49 ===
memory resource limit alarm cleared on node rabbit@VM_2_12_centos

通過錯誤日誌我們可以很明顯的看出,是MQ的運行內存超過限制,導致連接阻塞的。

問題原因:

因爲RabbitMQ服務器在啓動時會計算系統內存總大小。然後會根據vm_memory_high_watermark參數指定的百分比,進行控制.可通過命令 rabbitmqctl set_vm_memory_high_watermark fraction 動態設置。默認下,vm_memory_high_watermark的值爲0.4,當RabbitMQ使用的內存超過40%時,系統會阻塞所有連接。一旦警報解除(消息被消費者取走,或者消息被寫到硬盤中),系統重新恢復工作。32位系統的單進程內存使用最大爲2G,即使系統有10G,那麼內存警報線爲2G*40%.

解決方案:

最快的解決方案時將vm_memory_high_watermark值上調,如果短時間內上調不了,可以選擇直接升級服務器內存。

但是這樣治標不治本,如果隊列繼續堆積,內存佔用率還是會變大,最終的解決方案,應該要平衡消費者和生產者,讓消息隊列儘量不要堆積導致內存過大阻塞。

其它阻塞的場景:

硬盤控制

當RabbitMQ的磁盤空閒空間小於50M(默認),生產者將被BLOCK,並且阻塞信息發佈前,會嘗試把內存中的信息輸出到磁盤上。持久化信息和非持久化信息都將被寫到磁盤(持 久化信息一進入隊列就會被寫到磁盤)。如果採用集羣模式,磁盤節點空閒空間小於50M將導致其他節點的生產者都被block。可以通過disk_free_limit來對進行配置。 如果磁盤的預設值爲50%,內存預設值默認是0.4,那麼就是當內存使用量達到20%時,隊列信息會被寫到磁盤上。 可以通過設置 vm_memory_high_watermark_paging_ratio (默認值爲0.5)來改變寫入磁盤的策略,例如:

[{rabbit, [{vm_memory_high_watermark_paging_ratio, 0.75},          {vm_memory_high_watermark, 0.4}]}].1

上面的配置將會在內存全用30%時將隊列信息寫入到磁盤,阻塞信息發佈是在內存使用40%時發生。建議vm_memory_high_watermark 不超過50%.

消息積壓

在RabbitMQ中,消息可能被存儲在多個不同的隊列,消息越早被消費,那麼消息經過的隊列層次越少,則平均每個消息處理的開銷就越小。但若發佈消息的速率過快,MQ來不及處理,這些消息就可能進入很深層次的隊列,大大增加平均每個消息的處理開銷,進一步使得處理新消息和發送舊消息的能力減弱,更多的消息會進入很深的隊列,循環往復,整個系統的性能就會極大的降低。另外若接收消息的速率過快還會實現某些進程的mailbox過大,可能會產生很嚴重的後果。爲此,RabbitMQ設計了一套流控機制。

RabbitMQ 使用了一種基於 credit 的算法來 限制 message 被 publish 的速率 。Publisher 只有在其從某個 queue 的 全部鏡像處收到 credit 之後才被允許繼續 publish 。在這個上下文中,Credit 意味着對 publish 行爲的允許。如果存在沒能成功發送出 credit 的 slaves ,則將導致 publisher 停止 publish 動作。Publisher 會一直保持停止的狀態,直到所有 slave 都成功發送了 credit 或者直到剩餘的 node 都認爲某 slave 已經從 cluster 中斷開了。Erlang 會週期性地發送 tick 到所有的 node 上來檢測是否出現連接斷開。 tick 的間隔時間可以通過配置 net_ticktime 的值來控制。

想學習分佈式、微服務、JVM、多線程、架構、java、python的童鞋,千萬不要掃碼,否則後果自負~

林老師帶你學編程https://wolzq.com

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