博客 / 詳情

返回

限流系列之四:TDMQ RocketMQ 版限流機制詳解與實踐教程

導語

隨着分佈式系統架構的普及,消息隊列已成為支撐大規模、高併發在線業務的核心組件之一。TDMQ RocketMQ 版作為一款高性能、高可靠的消息中間件,通過提供穩定、低延遲的消息服務,幫助企業輕鬆應對業務洪峯、實現系統解耦。然而,在高併發、大流量場景下,如何合理分配資源、防止系統過載成為保障服務穩定性的關鍵。為此,TDMQ RocketMQ 版引入了分佈式限流機制,通過動態調整客户端的發送與消費速率,確保集羣在高負載情況下依然能夠穩定運行。

本文將詳細解析 TDMQ RocketMQ 版的限流機制,包括限流行為和限流實現原理。同時,結合實際案例,提供客户端實踐教程,幫助開發者更好地理解並應用限流機制,避免因集羣流控導致的業務受損。

概述

TDMQ RocketMQ 版為各類大規模、低延時、高可用性要求的在線業務提供消息服務,客户端通過 SDK 與 RocketMQ 集羣建立長連接並進行消息收發,同時消耗集羣機器節點的計算、存儲、網絡帶寬等資源。為了提供高質量的消息服務,我們需要控制集羣在高併發、大流量情況下的負載水位,以保障系統的穩定性與可靠性。因此,服務端會根據集羣規格限制客户端每秒能夠發送和消費的最大折算消息條數(TPS),消息的折算規則如下:

維度 折算規則
消息類型 普通消息:發送或者消費 1 條普通消息,按 1TPS 計 。高級特性消息(定時和延時消息、事務消息、順序消息):發送 1 條或者消費 1 條高級特性消息,按 5TPS 計算。
消息大小 消息大小以 4KB 為計量單位,每 4KB 計 1 TPS,不滿 4KB 按 4KB 計算。

為了兼具隔離性和靈活性,發送消息與消費消息的 TPS 配額不共享,同時支持自定義配額比例(默認配額比例為1 : 1 )。

限流行為

TDMQ RocketMQ 版採用快速失敗 (Fail-Fast) 的限流策略,即當客户端請求速率達到上限後,服務端會立即響應錯誤。通常在線業務都對響應時間敏感,快速失敗可以讓客户端感知限流事件並及時介入處理,避免業務消息端到端時延惡化。

以 1000TPS 規格的基礎集羣為例(假設收發 TPS 比例為1:1),客户端視角下的限流行為:

説明 發送消息限流 消費消息限流
觸發限流情景 所有連接該集羣的發送客户端每秒最多可發送折算消息的總和為 500 條,發送速率達到限制後,超限的發送請求會失敗。 所有連接該集羣的消費客户端每秒最多可消費折算消息的總和為 500 條,消費速率達到限制後,消息的消費延遲會增加。
觸發限流時 SDK 日誌關鍵詞 Rate of message sending reaches limit, please take a control or upgrade the resource specification。 Rate of message receiving reaches limit, please take a control or upgrade the resource specification。
觸發限流時 SDK 重試機制 不同協議的 SDK 處理有差異: 5.x SDK:會根據指數退避策略進行重試發送,最大重試次數可在初始化 Producer 時自定義,默認值為 2 次;達到最大重試次數仍未成功的發送請求會拋出異常。4.x SDK:直接拋出異常,不會進行重試。 SDK 拉消息線程會自動退避重試。

限流實現

分佈式限流

限流主要分為單機限流和分佈式限流兩種模式:單機限流用於節點自我過載保護,防止資源(CPU、內存、線程等)被耗盡;而分佈式限流則用於集羣環境,通過協調多節點流量來保護後端系統和共享資源。

TDMQ RocketMQ 版通過在計算層 (Proxy) 接入分佈式限流系統 (Limiter) 實現集羣級讀寫流量控制,其核心機制是:Proxy 節點在處理客户端 SendMessage / PullMessage 請求前需向 Limiter 申請 Token,若申請失敗則立即拒絕請求並返回錯誤。Proxy 內部集成了 Limiter SDK,該 SDK 提供 Token 申請 API,並負責與 Limiter Server 通信,通過這種集中式 Token 管理實現對核心存儲層 (Broker) 的保護。

平衡性能與精度

使用 TDMQ RocketMQ 版的各類在線業務通常對時延比較敏感,如果 Proxy 處理每次讀寫請求都執行一次 Limiter RPC 調用 (SDK -> Server),雖然 Limiter Server 內部處理耗時幾乎可以忽略,但兩次 RPC 的網絡 IO 耗時對消息端到端時延的影響不能忽視。

實際上從服務端的角度看,TDMQ RocketMQ 版執行限流的主要目的是防止核心存儲層過載,而非追求 100% 精準的流量控制,即 SDK 與 Server 之間的強同步並不是必須的。因此,為了在限流性能和限流精度之間取得平衡,Limiter 採用了一種【先消費後結算】的 Token 管理機制:Token 申請過程在 SDK 內部閉環,SDK 會週期性地向 Server 上報 Token 使用量並刷新配額。在這種機制下:

  • 執行限流是純內存操作,不會影響 TDMQ RocketMQ 版核心主鏈路時延。
  • 先消費後結算的機制保證不會出現誤限流。
  • 部分場景可能會出現短暫超限,但服務端資源 Buffer 可以抵消風險。
  • 如果 Limiter Server 因故障無法提供服務,則自動降級為單機限流,即 TDMQ RocketMQ 版對 Limiter Server 服務弱依賴。

計數週期

TDMQ RocketMQ 版集羣以 TPS 作為規格單位,例如 1000TPS 表示每秒可讀寫 1000 條折算消息。在限流機制中,這相當於每一秒分配 1000 個 Token,而“一秒”即為默認的限流計數週期。

實際運維中發現,部分集羣雖然整體流量未超限,但偶爾因業務流量短暫突增(毛刺)會觸發流控。限流計數週期的長短與對流量毛刺的敏感度成反比:週期越長,系統對毛刺的容忍度越高,但服務端資源面臨更高衝擊風險;週期越短,流控響應更嚴格,但可能誤傷正常業務波動。

為了儘可能提升用户使用體驗,我們將默認限流計數週期從一秒調整為十秒,這一調整顯著降低了因毛刺而觸發流控的頻率,同時服務端資源水位仍然安全可控。

客户端實踐教程

規劃集羣

TDMQ RocketMQ 版集羣限流的目的是保障服務穩定可靠,防止在集羣高負載時出現服務響應時間變長、請求成功率下降等問題,從而避免業務受損。因此,在您接入  TDMQ RocketMQ 版時,合理規劃集羣非常重要,建議:

  • 依據當前規模和未來趨勢預測來充分評估業務 TPS, 如果業務流量具有波動特性,應以峯值 TPS 為準。此外,評估時建議您預留一部分 TPS 配額(例如 30%)來應對可能出現的突發流量。
  • 對穩定性要求較高的業務,建議您使用多套 RocketMQ 集羣加強隔離性。例如將核心鏈路(如交易系統)與非核心鏈路(如日誌系統)隔離,以及生產環境與開發測試環境進行隔離等。

監控負載

您可以利用 TDMQ RocketMQ 版控制枱的監控告警能力實現對集羣負載的實時觀測,提前發現 TPS 水位風險並及時操作升配,保證資源充足,避免觸發限流。告警策略建議:

  • 發送和消費 TPS 水位超過容量的 70% 時觸發告警,提醒進行升配評估。
  • 出現發送限流時觸發告警,警告業務發送消息可能失敗風險。

示例

以 1000TPS 規格的基礎集羣為例,TPS 告警策略:

開啓彈性 TPS

當您的業務流量在大部分時間保持平穩,但偶爾出現峯值時,建議開啓 TDMQ RocketMQ 版的彈性 TPS 能力。開啓該功能後,服務端會在原有規格的基礎上,根據實際流量在彈性區間內自動伸縮資源,確保突發流量的穩定處理。

以 4000TPS 規格的專業版集羣為例,開啓彈性 TPS 後集羣限制最高可提升至 6500TPS:

  • 0 ≤ 實際流量 ≤ 4000TPS,不產生額外費用。
  • 4000TPS < 實際流量 ≤ 6500TPS,超出 4000TPS 的部分計算彈性費用。
  • 6500TPS < 實際流量,彈性費用按 2500TPS 計,超出 6500TPS 的部分被限流。

代碼異常處理

業務代碼通過 RocketMQ SDK 發送消息時,需要捕獲包括限流錯誤在內的異常,並保存必要的上下文信息,以便人工介入恢復業務。不同協議的 SDK 重試機制有差異,相關處理示例代碼如下:

4.x SDK 不會對限流錯誤進行自動重試,因此業務代碼需要捕獲異常並進行處理,示例代碼如下:


// 注:以下僅為示例代碼,運行需要額外的初始化代碼等
// 最大嘗試發送次數, 請根據業務情況設置
final int maxAttempts = 3;
// 重試間隔時間, 請根據業務情況設置
final int retryIntervalMillis = 200;
// 發送消息
int attempt = 0;
do {
    try {
        SendResult sendResult = producer.send(message);
        log.info("Send message successfully, {}", sendResult);
        break;
    } catch (Throwable t) {
        attempt++;
        if (attempt >= maxAttempts) {
            // 達到最大次數
            log.warn("Failed to send message finally, run out of attempt times, attempt={}, maxAttempts={}, msgId={}",
                attempt, maxAttempts, MessageClientIDSetter.getUniqID(message), t);
            // 記錄發送失敗的消息 (或記錄到其他業務系統, 比如數據庫等)
            log.warn(message.toString());
            break;
        }
        int waitMillis;
        if (t instanceof MQBrokerException && ((MQBrokerException) t).getResponseCode() == 215 /* FLOW_CONTROL */) {
            // 限流異常, 採用退避重試
            waitMillis = (int) Math.pow(2, attempt - 1) * retryIntervalMillis; // 重試間隔: 200ms, 400ms, ......
        } else {
            // 其他異常
            waitMillis = retryIntervalMillis;
        }
        log.warn("Failed to send message, will retry after {}ms, attempt={}, maxAttempts={}, msgId={}",
            waitMillis, attempt, maxAttempts, MessageClientIDSetter.getUniqID(message), t);
        try {
            Thread.sleep(waitMillis);
        } catch (InterruptedException ignore) {
        }
    }
}
while (true);

5.x SDK 會對發送異常進行自動重試,業務代碼可以自定義最大重試次數,示例代碼如下:

// 注:以下僅為示例代碼,運行需要額外的初始化代碼等
Producer producer = provider.newProducerBuilder()
    .setClientConfiguration(clientConfiguration)
    .setTopics(topicName)
    .setMaxAttempts(3) // 最大嘗試發送次數, 請根據業務情況設置
    .build();
try {
    final SendReceipt sendReceipt = producer.send(message);
    log.info("Send message successfully, messageId={}", sendReceipt.getMessageId());
} catch (Throwable t) {
    log.warn("Failed to send message", t);
    // 記錄發送失敗的消息 (或記錄到其他業務系統, 比如數據庫等)
    log.warn(message.toString());
}

常見問題

觸發限流後會不會丟消息?

發送消息觸發限流後服務端不會存儲該條消息,客户端需要捕獲異常並做降級處理;消費觸發限流後會出現消費延遲,但已經發送成功的消息不會丟。

為什麼監控頁面的 TPS 比消息條數大?

TPS 是折算消息數量,如果業務使用了高級消息(順序、延遲、事務等)或消息體比較大,那麼一條業務消息會被統計為多條折算消息。此外,消息條數指標統計的是一分鐘內的秒級平均值,而 TPS 指標統計的是一分鐘內的秒級峯值。

集羣偶爾出現短暫的消費被限流,是否有影響?

一般沒有影響。在客户端重啓、服務端重啓、控制枱擴容主題隊列等操作期間,都有可能因為消費組瞬間堆積而觸發短暫的消費限流,通常穩定後很快會恢復。

如何判斷集羣是否出現了限流?

除了通過識別 SDK 發送接口拋出的異常或 SDK 日誌記錄的信息外,您還可以查看TDMQ RocketMQ 控制枱的監控頁面 的被限流的生產 TPS(Count/s) 和被限流的消費 TPS(Count/s)

總結

TDMQ RocketMQ 版的限流機制為在線業務提供了穩定可靠的消息服務保障。在分佈式限流模式下,服務端通過集中式 Token 管理實現了對核心存儲層的保護,同時採用“先消費後結算”的 Token 管理機制,在限流性能和限流精度之間取得平衡。此外,面對 Limiter Server 服務不可用的情況,系統能夠自動降級為單機限流模式,確保客户端請求不受影響。

在實際應用中,開發者需要根據業務規模和未來趨勢合理規劃集羣,預留足夠的 TPS 配額以應對突發流量。對於業務流量大部分時間平穩但偶爾出現峯值的場景,可以通過開啓彈性 TPS 能力來應對偶發峯值並降低使用成本。同時,通過監控告警能力實時觀測集羣負載,提前發現 TPS 水位風險並及時進行升配操作。在業務代碼層面,需要捕獲限流異常並保存必要的上下文信息,以便人工介入恢復業務。

通過本文的介紹與實踐教程,相信讀者對 TDMQ RocketMQ 的限流機制有了更深入的理解,並能夠在實際項目中靈活應用這一機制,為業務的高併發、大流量場景提供有力支持。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.