流控

RabbitMQ能夠對內存和磁盤的使用量設置閾值,當到達閾值後,生產者將被阻塞,直到對應項恢復正常。除了這兩個閾值,從2.8.0版本開始,RabbitMQ還引入了流控(Flow Control)機制來確保穩定性。流控機制是用來避免消息的發送頻率過快而致使服務器難以支撐的情形。內存和磁盤告警至關於全局的流控,一旦觸發會阻塞集羣中全部的Connection,而流控是針對單個Connection的,能夠稱之爲Per-Connection Flow Control或者 Internal Flow Control。算法

流控的原理

Erlang進程之間並不共享內存(binary類型的除外),而是經過消息傳遞來通訊,每一個進程都有本身的進程郵箱(mailbox)。默認狀況下,Erlang並無對進程郵箱的大小進行限制,因此當有大量消息持續發往某個進程時,會致使該進程郵箱過大,最終內存溢出並崩潰。在RabbitMQ中,若是生產者持續高速發送,而消費者消費速度較低時,若是沒有流控,很快就會使內部進程郵箱的大小達到內存閾值。服務器

RabbitMQ使用了一種基於信用正算法(credit-based algorithm)的流控機制來限制發送消息的速率以解決這個問題。它經過監控各個進程的進程郵箱,當某個進程負載太高而來不及處理消息時,這個進程的郵箱就會開始慢慢堆積消息,當堆積到必定量時,就會阻塞而不接收上游的新消息。從而慢慢的,上游的進程郵箱也會開始慢慢的堆積消息。當堆積到必定量的時候也會阻塞而中止接收上游的消息,最後就會使負責網絡數據包接收的進程阻塞而暫停接收新數據。如下圖爲例:網絡

進程A接收消息並轉發至進程B,進程B接收消息並轉發至進程C。每一個進程中都有一對關於收發消息的credit值。以進程B爲例,{{credit_from,C},value}表示能發多少條消息給C,每發一條消息就將該值減1,當爲0時,進程B再也不往進程C發送消息,也再也不接收進程A的消息。{{credit_to,A},value}表示再接收多少條消息就向進程A發送增長credit值的通知,進程A接收到通知後就增長{{credit_from,B},value}所對應的值,這樣進程A就能持續發送消息。當上遊發送速率高於下游接收速率時,credit值就會逐漸被消耗光,這時進程就會被阻塞,阻塞的狀況會一直傳遞到最上游。當上遊進程收到來自下游的增長credit值的通知時,若此時上游處於阻塞狀態則解除最上游的阻塞狀態,開始接收更上游的進程消息,一個一個傳導最終可以解除最上游的阻塞狀態。因而可知,基於信用證的流控機制最終將消息發送進程的發送速率限制在消息處理進程的處理能力範圍以內。性能

一個鏈接觸發流控時會處於「flow」狀態,也就意味着這個Connection的狀態每秒在blocked和unblocked之間來回切換數次,這樣能夠將消息發送的速率控制在服務器可以支撐的範圍以內,能夠經過rabbitmqctl list_connections命令或者Web管理界面查看Connection的狀態。blog

處於flow狀態的Connection和處於running狀態並無什麼不一樣,這個狀態只是告訴管理員相應的發送速率受限了,而對於客戶端而言,它看到的只是服務器的帶寬要比正常狀況下小一些。rabbitmq

流控機制不僅是做用於Connection,一樣做用於信道和隊列。從Connection到Channel,再到隊列,最後是消息持久化存儲造成一個完整的流控鏈,對於處於整個流控鏈中的任意進程,只要該進程阻塞,上游的進程一定阻塞。也就是說,若是某個進程達到性能瓶頸,必然會致使上游全部的進程被阻塞。因此咱們能夠利用流控機制的這個特色找出瓶頸之所在,處理消息的幾個關鍵進程及其對應的順序以下圖:隊列

其中各個進程以下所述:進程

  ❤ rabbit_reader:Connection的處理進程,負責接收、解析AMQP協議數據包等;內存

  ❤ rabbit_channel:Channel的處理進程,負責處理AMQP協議的各類辦法、進行路由解析等;路由

  ❤ rabbit_amqqueue_process:隊列的處理進程,負責實現隊列的全部邏輯;

  ❤ rabbit_msg_store:負責實現消息的持久化;

當某個Connection處於flow狀態,但這個Connection中沒有一個Channel處於flow狀態時,這就意味着Connection中有一個或者多個Channel出現了性能瓶頸,某些Channel進程的運做(好比處理路由邏輯)會使得服務器CPU的負載太高而致使出現此種情形。尤爲是在發送大量較小的非持久化消息時,此種情形最易顯現。

當某個Connection處於flow狀態,而且這個Connection中也有若干個Channel處於flow狀態,但沒有任何一個對應的隊列處於flow狀態,這就意味着有一個或者多個隊列出現了性能瓶頸。這多是因爲將消息存入隊列的過程當中引發服務器CPU負載太高,或者將隊列中的消息存入磁盤的過程當中引發服務器I/O負載太高而引發此種情形。尤爲是在發送大量較小的持久化消息時,此種情形最易顯現。

當某個Connection處於flow狀態,同時這個Connection中也有若干Channel處於flow狀態,而且也有若干隊列處於flow狀態,這就意味着在消息持久化的時候出現了性能瓶頸。在將隊列中的消息存入磁盤的過程當中引發服務器I/O負載太高而引發的此種情形。尤爲是在發送大量較大的持久化消息時,此種情形最易顯現。

參考:《RabbitMQ實戰指南》 朱忠華 編著;

相關文章
相關標籤/搜索