消費者從云消息隊列 RocketMQ 版獲取消息消費時,通過消費者負載均衡策略,可將主題內(nèi)的消息分配給指定消費者分組中的多個消費者共同分擔,提高消費并發(fā)能力和消費者的水平擴展能力。本文介紹云消息隊列 RocketMQ 版消費者的負載均衡策略。

背景信息

了解消費者負載均衡策略,可以幫助您解決以下問題:
  • 消息消費處理的容災(zāi)策略:您可以根據(jù)消費者負載均衡策略,明確當局部節(jié)點出現(xiàn)故障時,消息如何進行消費重試和容災(zāi)切換。
  • 消息消費的順序性機制:通過消費者負載均衡策略,您可以進一步了解消息消費時,如何保證同一消息組內(nèi)消息的先后順序。
  • 消息分配的水平拆分策略:了解消費者負載均衡策略,您可以明確消息消費壓力如何被分配到不同節(jié)點,有針對性地進行流量遷移和水平擴縮容。

廣播消費和共享消費

云消息隊列 RocketMQ 版領(lǐng)域模型中,同一條消息支持被多個消費者分組訂閱,同時,對于每個消費者分組可以初始化多個消費者。您可以根據(jù)消費者分組和消費者的不同組合,實現(xiàn)以下兩種不同的消費效果:消費方式
  • 消費組間廣播消費:如上圖所示,每個消費者分組只初始化唯一一個消費者,每個消費者可消費到消費者分組內(nèi)所有的消息,各消費者分組都訂閱相同的消息,以此實現(xiàn)單客戶端級別的廣播一對多推送效果。

    該方式一般可用于網(wǎng)關(guān)推送、配置推送等場景。

  • 消費組內(nèi)共享消費:如上圖所示,每個消費者分組下初始化了多個消費者,這些消費者共同分擔消費者分組內(nèi)的所有消息,實現(xiàn)消費者分組內(nèi)流量的水平拆分和均衡負載。

    該方式一般可用于微服務(wù)解耦場景。

什么是消費者負載均衡

如上文所述,消費組間廣播消費場景下,每個消費者分組內(nèi)只有一個消費者,因此不涉及消費者的負載均衡。

消費組內(nèi)共享消費場景下,消費者分組內(nèi)多個消費者共同分擔消息,消息按照哪種邏輯分配給哪個消費者,就是由消費者負載均衡策略所決定的。

根據(jù)消費者類型的不同,消費者負載均衡策略分為以下兩種模式:

消息粒度負載均衡

使用范圍

對于PushConsumer和SimpleConsumer類型的消費者,默認且僅使用消息粒度負載均衡策略。

策略原理

消息粒度負載均衡策略中,同一消費者分組內(nèi)的多個消費者將按照消息粒度平均分攤主題中的所有消息,即同一個隊列中的消息,可被平均分配給多個消費者共同消費。

消息粒度負載

如上圖所示,消費者分組Group A中有三個消費者A1、A2和A3,這三個消費者將共同消費主題中同一隊列Queue1中的多條消息。

重要 消息粒度負載均衡策略保證同一個隊列的消息可以被多個消費者共同處理,但是該策略使用的消息分配算法結(jié)果是隨機的,并不能指定消息被哪一個特定的消費者處理。

消息粒度的負載均衡機制,是基于內(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),因此,為了避免消息被多個消費者重復消費,每個隊列僅支持被一個消費者消費。

重要 隊列粒度負載均衡策略保證同一個隊列僅被一個消費者處理,該策略的實現(xiàn)依賴消費者和服務(wù)端的信息協(xié)商機制,云消息隊列 RocketMQ 版并不能保證協(xié)商結(jié)果完全強一致。因此,在消費者數(shù)量、隊列數(shù)量發(fā)生變化時,可能會出現(xiàn)短暫的隊列分配結(jié)果不一致,從而導致少量消息被重復處理。

策略特點

相對于消息粒度負載均衡策略,隊列粒度負載均衡策略分配粒度較大,不夠靈活。但該策略在流式處理場景下有天然優(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)象。因此,需要在下游消費邏輯中做好消息冪等去重處理。