生产者发送消息到云消息队列 RocketMQ 版服务端时,云消息队列 RocketMQ 版将根据生产者负载均衡策略将消息均匀的存储在多个队列中,避免产生热点队列和性能瓶颈。本文介绍云消息队列 RocketMQ 版生产者的负载均衡策略。

背景信息

了解生产者负载均衡策略,可以帮助您解决如下问题:
  • 消息发送的容灾策略:您可以根据生产者负载均衡策略,明确当局部节点出现故障时,消息发送如何进行容灾切换。
  • 消息发送的顺序性机制:通过生产者负载均衡策略,您可以进一步了解顺序消息发送时,如何保证相同消息组内消息的先后顺序。
  • 消息服务能力的均衡性策略:了解生产者负载均衡策略,您可以知晓消息在不同节点间分配的规律,您可以按照负载策略针对性地设计流量迁移和水平扩缩容的方案。

RoundRobin模式

使用范围

对于非顺序消息(普通消息、定时/延时消息、事务消息)场景,默认且只能使用RoundRobin模式的负载均衡策略。

策略原理:轮询方式

RoundRobin模式下,生产者发送消息时,以消息为粒度,按照轮询方式将消息依次发送到指定主题中的所有可写目标队列中,保证消息尽可能均衡地分布到所有队列。

生产者负载策略

如上图所示,M1、M2表示生产者发送的第一条消息、第二条消息,Queue1、Queue2、Queue3表示主题中的三个队列。

生产者按照轮询方式分别将消息依次发送到这三个队列中,M1发送至Queue1中、M2发送至Queue2中、M3发送至Queue3中,以此类推,第四条消息M4又发送至Queue1中,循环往复。

异常处理

当发送某条消息发送失败时,云消息队列 RocketMQ 版会根据失败原因决定在接下来一段时间内,选择队列目标时跳过本地失败队列所在的节点,快速实现自适应的故障隔离。

策略特点

RoundRobin模式的生产者负载均衡策略仅适用于无顺序性的消息,该模式下是以消息为粒度进行轮询负载,因此消息能够尽可能实现均匀分布,使得主题的传输能力尽可能达到最大。

使用示例

RoundRobin模式不需要额外设置,对于非顺序类型的消息默认启用。

//普通消息发送模式默认采用RoundRobin负载均衡策略。

        //普通消息发送。
        MessageBuilder messageBuilder = null;
        for (int i = 0; i < 10; i++) {
            //普通消息发送时无需客户端额外设置,由SDK内置逻辑实现队列轮询和平均分配。
            Message message = messageBuilder.setTopic("normalTopic")
                    //设置消息索引键,可根据关键字精确查找某条消息。
                    .setKeys("messageKey")
                    //设置消息Tag,用于消费端根据指定Tag过滤消息。
                    .setTag("messageTag")
                    //消息体。
                    .setBody("messageBody".getBytes())
                    .build();
            try {
                //发送消息,需要关注发送结果,并捕获失败等异常。
                SendReceipt sendReceipt = producer.send(message);
                System.out.println(sendReceipt.getMessageId());
            } catch (ClientException e) {
                e.printStackTrace();
            }
        }


         

MessageGroupHash模式

使用范围

对于顺序消息场景,默认且只能使用MessageGroupHash模式的负载均衡策略。

策略原理:Hash算法

MessageGroupHash模式下,生产者发送消息时,以消息组为粒度,按照内置的Hash算法,将相同消息组的消息分配到同一队列中,保证同一消息组的消息按照发送的先后顺序存储。

说明 Hash算法的具体原理,请参见SipHash算法
hash算法分配

如上图所示,消息G1-M1、G1-M2、G1-M3属于消息组1中的第一条消息、第二条消息和第三条消息,生产者按照Hash算法将这几条消息分配到同一队列MessageQueue1中,且在队列中保存的先后顺序和发送顺序一致。

策略特点

MessageGroupHash模式的生产者负载均衡策略仅适用于顺序性的消息,可以很好地保证同消息组内消息的顺序性。

但是若不同消息组的消息数量差异较大,MessageGroupHash模式将不能很好地保障消息的均衡分配和性能扩展能力。在极端场景下,可能会出现大部分消息集中在少数队列中的情况,建议设计消息组时尽量将消息离散开,不要集中在少量消息组中。

使用示例

MessageGroupHash模式不需要额外设置,对于顺序消息类型默认启用。

//顺序消息发送,默认采用MessageGroupHash模式负载均衡策略。

for (int i = 0; i < 10; i++) {
            Message message = messageBuilder.setTopic("fifoTopic")
                    //设置消息索引键,可根据关键字精确查找某条消息。
                    .setKeys("messageKey")
                    //设置消息Tag,用于消费端根据指定Tag过滤消息。
                    .setTag("messageTag")
                    //顺序消息场景需要设置MessageGroup,相同MessageGroup的消息通过Hash算法会被分配到同一个队列。
                    .setMessageGroup("fifoGroupA")
                    //消息体。
                    .setBody("messageBody".getBytes())
                    .build();
            try {
                //发送消息,需要关注发送结果,并捕获失败等异常。
                SendReceipt sendReceipt = producer.send(message);
                System.out.println(sendReceipt.getMessageId());
            } catch (ClientException e) {
                e.printStackTrace();
            }
        }

版本兼容性

  • MessageGroupHash模式

    MessageGroupHash模式的负载均衡策略从云消息队列 RocketMQ 版服务端5.0版本开始支持,历史版本4.x/3.x版本不支持。

    因此,若将顺序消息的发送机制从历史4.x/3.x版本升级到5.x版本时,您需要自行保证消息的顺序性。例如,您可以将主题内的消息消费完,再切换到新的5.x版本上。

  • RoundRobin模式

    RoundRobin模式的负载均衡策略服务端5.0版本和历史版本4.x/3.x版本均支持,不涉及版本兼容性。

使用建议

使用MessageGroupHash模式时,避免出现热点队列

MessageGroupHash模式下,云消息队列 RocketMQ 版保证相同消息组的消息存储在同一个队列中。如果业务侧将消息都集中在少量或唯一的消息组,则此时服务端存储消息时,也会集中存储在少量或唯一的队列中。极大增加了服务端的存储压力,导致出现队列热点,不利于主题处理能力的水平扩展。

因此,建议您在设计消息组时,尽量将消息分散开。例如,采用较离散的订单ID、用户名作为消息组的关键字,既能保证消息被分散到多个消息组中,又能保证同一终端用户的消息按顺序处理。

避免绑定单队列发送

不管顺序消息还是非顺序消息,为了保证服务端性能的水平扩展和容灾能力,建议都尽可能使用多个队列,尽量避免负载均衡后只使用单个队列。例如,若某主题只有一个队列,则不管怎么负载均衡,消息也只能发送到这一个队列中,单个队列容易产生性能瓶颈及容灾风险。