RabbitMQ 客戶端源碼系列 - Flow Controller 原理
前言
這次分享 RabbitMQ 自帶的保護(hù) RabbitMQ 免于過載的功能 - Flow Controller(「流量控制」),如果不明白原理和場景使用 RabbitMQ 時,遇到 Flow Controller 容易一臉懵逼不知所措,今天我們就來了解它的原理。
什么是流量控制?
流控制是一個在計算機(jī)網(wǎng)絡(luò)和網(wǎng)絡(luò)軟件中存在了幾十年的概念。本質(zhì)上,它是一種向發(fā)送方施加背壓以避免接收方過載的機(jī)制。接收器通常緩沖傳入的數(shù)據(jù)包/消息,作為處理超過其處理速率的發(fā)送速率的一種方式。但是接收器緩沖區(qū)不能永遠(yuǎn)增長,因此發(fā)送速率只能暫時超過接收器處理能力(突發(fā)流量),或者發(fā)送器必須放慢速度(背壓)。
流量控制是一種向發(fā)送方施加這種背壓的方法,減慢它們的速度,以便接收方的緩沖區(qū)不會溢出并且延遲不會變得太大。在發(fā)送方/接收方鏈中,這種背壓可以沿鏈向上傳播到流量的源頭。在更復(fù)雜的連接組件圖中,流控制可以平衡快速和慢速發(fā)送方之間的傳入流量,避免過載,但允許系統(tǒng)在不同數(shù)量的發(fā)送方、不同的速率和不同的負(fù)載模式(穩(wěn)定或突發(fā))下達(dá)到充分利用。
RabbitMQ 中的流量控制
RabbitMQ 看起來很像一個網(wǎng)絡(luò)。每個 RabbitMQ Broker 在內(nèi)部都使用 actor 模式實現(xiàn),其中不同的組件通過消息傳遞相互通信,有時是在本地,有時是通過網(wǎng)絡(luò)。還有發(fā)布者通過網(wǎng)絡(luò)向代理發(fā)送消息,消費者從代理接收消息。
消息流的簡化描述
將系統(tǒng)作為一個整體(代理和客戶端),我們有四種可用的流量控制機(jī)制:
- 基于信用的流量控制。
- 內(nèi)存報警。
- 發(fā)布者確認(rèn)。
- 消費者確認(rèn)和預(yù)取。
基于信用的流量控制
基于信用的流量控制是一種限制消息進(jìn)入速率的方法。它允許系統(tǒng)內(nèi)的各個參與者保護(hù)自己并在他們無法足夠快地處理消息時施加反壓。它僅針對那些有問題的連接、通道和隊列,而系統(tǒng)的其他部分不受影響。
它的工作方式是系統(tǒng)中處理消息的每個參與者都使用“信用”作為向鏈?zhǔn)┘颖硥旱囊环N方式。如果通道想要向隊列發(fā)送消息,它需要信用。隊列授予通道一些初始信用,然后,通道發(fā)送到隊列的每條消息都需要一個信用。隊列將定期授予通道更多的信用,當(dāng)它反過來能夠?qū)⑾鬟f到持久層時。如果通道沒有信用,它會被阻止向隊列發(fā)送消息,直到隊列授予它更多。這樣通道就不能粗暴地運行在隊列上。
經(jīng)典隊列的基于信用的流量控制
所以我們有一個信用流控制鏈,可以一直向發(fā)布者施加背壓。最終,TCP 背壓將施加到發(fā)布者,因為 TCP 讀取器進(jìn)程在被阻塞時不會從套接字讀取。
當(dāng)連接、通道或隊列用完信用時,它們會被阻塞,直到授予更多信用,這種狀態(tài)稱為“流”。在管理 UI 中,您可能會看到連接、通道或隊列處于流狀態(tài),這表明流最近發(fā)生。這只是意味著他們暫時用完了信用,正在等待鏈中的下一個環(huán)節(jié)趕上并授予一些信用。這可以每秒觸發(fā)多次。
信用枯竭
信貸贈款
當(dāng)隊列或連接達(dá)到其吞吐量限制或下游瓶頸時,流狀態(tài)可以在鏈中的各個點每秒多次觸發(fā),因為各個參與者的信用額度達(dá)到 0 然后得到補(bǔ)充。
但這并不一定會阻止代理耗盡內(nèi)存。傳入消息并不總是高內(nèi)存使用率的唯一主要原因,它也可能來自大隊列和許多其他原因。
內(nèi)存警報
如果基于信用的流量控制無法充分剎車,或者內(nèi)存使用量由于其他原因增長到臨界水平,則內(nèi)存警報將作為最后的手段啟動,以保護(hù)代理免于崩潰(或被操作系統(tǒng)殺死)內(nèi)存不足。
當(dāng)內(nèi)存警報開始時,所有發(fā)布者都會被阻止。這就像您關(guān)閉了跨集群的傳入消息的水龍頭。不是基于信用的流量控制的目標(biāo)速率限制,而是大錘。
消費者可以繼續(xù)消費,此時希望排空隊列會開始減少內(nèi)存占用。
在管理 UI 中,當(dāng)內(nèi)存警報生效時,您將看到連接被阻止或阻塞。
Memory
https://www.rabbitmq.com/memory.html。
發(fā)布者確認(rèn)
發(fā)布者確認(rèn)的主要工作是數(shù)據(jù)安全,但它們在流量控制中也起著重要作用。
有以下三種方式使用發(fā)布者確認(rèn):
- 一次發(fā)送一個,在發(fā)送下一個之前等待每個確認(rèn)(非常慢)。
- 基于窗口的. 發(fā)送消息直到達(dá)到窗口大小(時間或消息數(shù)量)并在發(fā)送下一個窗口之前等待所有確認(rèn)。
- 流水線。允許發(fā)布者連續(xù)發(fā)送消息,但在未確認(rèn)的消息計數(shù)(傳輸中的消息)達(dá)到限制時阻止。當(dāng)確認(rèn)進(jìn)來時,可以發(fā)送更多消息,直到再次達(dá)到限制。
流水線(或簡稱異步)方法提供最高和最穩(wěn)定的吞吐量。它可以用作防止經(jīng)紀(jì)人過載的額外保護(hù),因為發(fā)布者本身甚至在對經(jīng)紀(jì)人施加壓力之前就將自己置于“流”中。
當(dāng)您不使用發(fā)布者確認(rèn)時,您僅依靠 TCP 流控制來控制發(fā)布者和代理上的連接讀取器進(jìn)程之間的鏈接。在發(fā)布者數(shù)量相對較少的情況下,TCP 流量控制足以避免代理過載,但是當(dāng)您有大量客戶端時,TCP 是不夠的,并且發(fā)布者確認(rèn)在重負(fù)載下對于集群穩(wěn)定性變得必要。有趣的是,AMQP 1.0 添加了鏈路流控制來克服這個問題。
Publisher confirms
https://www.rabbitmq.com/confirms.html。
消費者確認(rèn)和預(yù)取
使用帶有預(yù)取的手動確認(rèn)會給 RabbitMQ 帶來壓力,以阻止它使您的消費者客戶端不堪重負(fù)。它使用流水線方法發(fā)送恒定的消息流,但將未確認(rèn)消息的數(shù)量限制為預(yù)取 (QoS) 的大小。使用 AutoAck 模式,我們再次僅依賴 TCP 背壓。客戶端的各種入口緩沖區(qū)可能會很快填滿。