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