消費者從雲訊息佇列 RocketMQ 版擷取訊息消費時,通過消費者負載平衡策略,可將主題內的訊息分配給指定消費者分組中的多個消費者共同分擔,提高消費並發能力和消費者的水平擴充能力。本文介紹雲訊息佇列 RocketMQ 版消費者的負載平衡策略。
背景資訊
- 訊息消費處理的容災策略:您可以根據消費者負載平衡策略,明確當局部節點出現故障時,訊息如何進行消費重試和容災切換。
- 訊息消費的順序性機制:通過消費者負載平衡策略,您可以進一步瞭解訊息消費時,如何保證同一訊息組內訊息的先後順序。
- 訊息分配的水平分割策略:瞭解消費者負載平衡策略,您可以明確訊息消費壓力如何被分配到不同節點,有針對性地進行流量遷移和水平擴縮容。
廣播消費和共用消費
- 消費組間廣播消費:如上圖所示,每個消費者分組只初始化唯一一個消費者,每個消費者可消費到消費者分組內所有的訊息,各消費者分組都訂閱相同的訊息,以此實現單用戶端層級的廣播一對多推送效果。
該方式一般可用於網關推送、配置推送等情境。
- 消費組內共用消費:如上圖所示,每個消費者分組下初始化了多個消費者,這些消費者共同分擔消費者分組內的所有訊息,實現消費者分組內流量的水平分割和均衡負載。
該方式一般可用於微服務解耦情境。
什麼是消費者負載平衡
如上文所述,消費組間廣播消費情境下,每個消費者分組內只有一個消費者,因此不涉及消費者的負載平衡。
消費組內共用消費情境下,消費者分組內多個消費者共同分擔訊息,訊息按照哪種邏輯分配給哪個消費者,就是由消費者負載平衡策略所決定的。
訊息粒度負載平衡
使用範圍
對於PushConsumer和SimpleConsumer類型的消費者,預設且僅使用訊息粒度負載平衡策略。
策略原理
訊息粒度負載平衡策略中,同一消費者分組內的多個消費者將按照訊息粒度平均分攤主題中的所有訊息,即同一個隊列中的訊息,可被平均分配給多個消費者共同消費。
如上圖所示,消費者分組Group A中有三個消費者A1、A2和A3,這三個消費者將共同消費主題中同一隊列Queue1中的多條訊息。
訊息粒度的負載平衡機制,是基於內部的單條訊息確認語義實現的。消費者擷取某條訊息後,服務端會將該訊息加鎖,保證這條訊息對其他消費者不可見,直到該訊息消費成功或消費逾時。因此,即使多個消費者同時消費同一隊列的訊息,服務端也可保證訊息不會被多個消費者重複消費。
順序訊息負載機制
在順序訊息中,訊息的順序性指的是同一訊息組內的多個訊息之間的先後順序。因此,順序訊息情境下,訊息粒度負載平衡策略還需要保證同一訊息組內的訊息,按照服務端儲存的先後順序進行消費。不同消費者處理同一個訊息組內的訊息時,會嚴格按照先後順序鎖定訊息狀態,確保同一訊息組的訊息串列消費。
如上圖所述,隊列Queue1中有4條順序訊息,這4條訊息屬於同一訊息組G1,儲存順序由M1到M4。在消費過程中,前面的訊息M1、M2被消費者Consumer A1處理時,只要消費狀態沒有提交,消費者A2是無法並行消費後續的M3、M4訊息的,必須等前面的訊息提交消費狀態後才能消費後面的訊息。
策略特點
相對於隊列粒度負載平衡策略,訊息粒度負載平衡策略有以下特點:
- 消費分攤更均衡
傳統隊列級的負載平衡策略中,如果隊列數量和消費者數量不均衡,則可能會出現部分消費者空閑,或部分消費者處理過多訊息的情況。訊息粒度負載平衡策略無需關注消費者和隊列的相對數量,能夠更均勻地分攤訊息。
- 對非對等消費者更友好
對於線上生產環境,由於網路機房分區延遲、消費者實體資源規格不一致等原因,消費者的處理能力可能會不一致,如果按照隊列分配訊息,則可能出現部分消費者訊息堆積、部分消費者閒置情況。訊息粒度負載平衡策略按需分配,消費者處理任務更均衡。
- 隊列分配營運更方便
傳統基於綁定隊列的負載平衡策略,必須保證隊列數量大於等於消費者數量,以免產生部分消費者擷取不到隊列出現空轉的情況,而訊息粒度負載平衡策略則無需關注隊列數。
適用情境
訊息粒度消費負載平衡策略下,同一隊列內的訊息離散地分佈於多個消費者,適用於絕大多數線上事件處理的情境。只需要基本的訊息處理能力,對訊息之間沒有批量彙總的訴求。而對於串流、彙總計算情境,需要明確地對訊息進行彙總、批處理時,更適合使用隊列粒度的負載平衡策略。
使用樣本
訊息粒度負載平衡策略不需要額外設定,對於PushConsumer和SimpleConsumer消費者類型預設啟用。
SimpleConsumer simpleConsumer = null;
//消費樣本一:使用PushConsumer消費普通訊息,只需要在消費監聽器處理即可,無需關注訊息負載平衡。
MessageListener messageListener = new MessageListener() {
@Override
public ConsumeResult consume(MessageView messageView) {
System.out.println(messageView);
//根據消費結果返回狀態。
return ConsumeResult.SUCCESS;
}
};
//消費樣本二:使用SimpleConsumer消費普通訊息,主動擷取訊息處理並提交。會按照訂閱的主題自動擷取,無需關注訊息負載平衡。
List<MessageView> messageViewList = null;
try {
messageViewList = simpleConsumer.receive(10, Duration.ofSeconds(30));
messageViewList.forEach(messageView -> {
System.out.println(messageView);
//消費處理完成後,需要主動調用ACK提交消費結果。
try {
simpleConsumer.ack(messageView);
} catch (ClientException e) {
e.printStackTrace();
}
});
} catch (ClientException e) {
//如果遇到系統流控等原因造成拉取失敗,需要重新發起擷取訊息請求。
e.printStackTrace();
}
隊列粒度負載平衡
使用範圍
對於歷史版本(服務端4.x/3.x版本)的消費者,包括PullConsumer、DefaultPushConsumer、DefaultPullConsumer、LitePullConsumer等,預設且僅能使用隊列粒度負載平衡策略。
策略原理
隊列粒度負載平衡策略中,同一消費者分組內的多個消費者將按照隊列粒度消費訊息,即每個隊列僅被一個消費者消費。
如上圖所示,主題中的三個隊列Queue1、Queue2、Queue3被分配給消費者分組中的兩個消費者,每個隊列只能分配給一個消費者消費,該樣本中由於隊列數大於消費者數,因此,消費者A2被分配了兩個隊列。若隊列數小於消費者數量,可能會出現部分消費者無綁定隊列的情況。
隊列粒度的負載平衡,基於隊列數量、消費者數量等運行資料進行統一的演算法分配,將每個隊資料行繫結到特定的消費者,然後每個消費者按照取訊息>提交消費位點>持久化消費位點的消費語義處理訊息,取訊息過程不提交消費狀態,因此,為了避免訊息被多個消費者重複消費,每個隊列僅支援被一個消費者消費。
策略特點
相對於訊息粒度負載平衡策略,隊列粒度負載平衡策略分配粒度較大,不夠靈活。但該策略在串流情境下有天然優勢,能夠保證同一隊列的訊息被相同的消費者處理,對於批量處理、彙總處理更友好。
適用情境
隊列粒度負載平衡策略適用於流式計算、資料彙總等需要明確對訊息進行彙總、批處理的情境。
使用樣本
隊列粒度負載平衡策略不需要額外設定,對於歷史版本(服務端4.x/3.x版本)的消費者類型PullConsumer預設啟用。
具體範例程式碼,請訪問RocketMQ程式碼程式庫擷取。
版本相容性
訊息粒度的負載平衡策略從雲訊息佇列 RocketMQ 版服務端5.0版本開始支援,歷史版本4.x/3.x版本僅支援隊列粒度的負載平衡策略。
當您使用的雲訊息佇列 RocketMQ 版服務端版本為5.x版本時,兩種消費者負載平衡策略均支援,具體生效的負載平衡策略依用戶端版本和消費者類型而定。
使用建議
針對消費邏輯做訊息等冪
無論是訊息粒度負載平衡策略還是隊列粒度負載平衡策略,在消費者上線或下線、服務端擴縮容等情境下,都會觸發短暫的重新負載平衡動作。此時可能會存在短暫的負載不一致情況,出現少量訊息重複的現象。因此,需要在下遊消費邏輯中做好訊息等冪去重處理。