一、什么是负载均衡
负载均衡(LB,Load Balance),是一种技术解决方案。用来在多个资源(一般是服务器)中分配负载,达到最优化资源使用,避免单台服务器过载。
RocketMQ中的负载均衡,主要可以分为Producer发送消息的负载均衡和Consumer订阅消息的负载均衡。下面我们分别进行分析。
二、Producer发送消息负载均衡
Producer端,每个实例在发消息的时候,默认会通过轮询队列的方式发送,以达到让消息平均落在不同的队列上,即每个队列接收平均的消息量。
注:另外多个队列可以部署在一台机器上,也可以分别部署在多台不同的机器上。
如图所示,5 个队列可以部署在一台机器上,也可以分别部署在 5 台不同的机器上,发送消息通过轮询队列的方式发送,每个队列接收平均的消息量。通过增加机器,可以水平扩展队列容量。 另外也可以自定义方式选择发往哪个队列。
注:RocketMQ的顺序消息发送的时候,就要求我们自己实现队列选择器,根据消息唯一标识选择对应的队列进行发送。
三、Consumer订阅消息负载均衡
注意,RocketMQ消费模式有集群消费和广播消费,因为广播模式所有的Consumer都会收到全量消息,所以RocketMQ的负载均衡只针对于Consumer集群消费的模式。
如图所示,如果有 5 个队列,2 个 consumer,那么第一个 Consumer 消费 3 个队列,第二 consumer 消费 2 个队列。 这样即可达到平均消费的目的,可以水平扩展 Consumer 来提高消费能力。但是 Consumer 数量要小于等于队列数量,如果 Consumer 超过队列数量,那么多余的 Consumer 将不能消费消息,也就无法起到分摊负载的作用,所以需要控制让queue的总数量大于等于consumer的数量。
这里扩展一下如何提高Consumer的消费并行度?
(1)同一个Consumer Group下,通过增加Consumer实例数量来提高并行度,不过需要注意,超过订阅队列数的Consumer实例无效。可以通过加机器,或者在已有机器启动多个进程的方式。
(2)提高单个 Consumer 的消费并行线程,通过修改设置 consumeThreadMin最小并发线程数和consumeThreadMax最大并发线程数来提高消费能力。
(3)通过设置Consumer的consumeMessageBatchMaxSize这个参数,默认是1,即一次只消费一条消息,例如设置为N,那么每次消费的消息数小于等于N。这样即可大幅度提高消费的吞吐量。
四、Rebalance机制
(1)、什么是Rebalance?
Rebalance即再均衡,指的是将⼀个Topic下的多个Queue在同⼀个Consumer Group中的多个 Consumer间进行重新分配的过程,它能够提升消息的并行消费能力。
(2)、哪些场景会触发Rebalance?
a、消费者所订阅Topic的队列数量发生变化
比如我们动态调整了Topic对应的队列数量,那么此时肯定是要重新分配一下,也就是触发Rebalance再均衡。例如⼀个Topic下5个队列,有2个消费者的情况下,那么就可以给其中⼀个消费者分配2个队列,给另⼀个分配3个队列;假设我们调整到Topic下有7个队列,还是2个消费者的情况下,那么就可以给其中⼀个消费者分配4个队列,给另⼀个分配3个队列;从而提升消息的并行消费能力。如下图:
像Broker扩容或缩容、Broker与NameServer间发生网络异常、Queue扩容或缩容等场景都可能导致消费者所订阅Topic的队列数量发生变化。
b、消费者组中消费者的数量发生变化
比如我们动态添加了Consumer进行消费,那么此时肯定是要重新分配一下,也就是触发Rebalance再均衡。例如,⼀个Topic下5个队列,在只有1个消费者的情况下,这个消费者将负责消费这5个队列的消息。如果此时我们增加⼀个消费者,那么就可以给 其中⼀个消费者分配2个队列,给另⼀个分配3个队列,从而提升消息的并行消费能力。 如下图:
像Consumer Group扩容或缩容、Consumer与NameServer间发生网络异常、Consumer发生宕机等都会导致消费者组中消费者的数量发生变化。需要注意的是,由于⼀个队列最多分配给⼀个消费者,因此当某个消费者组下的消费者实例数量大于队列的数量时, 多余的消费者实例将分配不到任何队列,等于是多余的消费者什么都不做,白白浪费。
(3)、Rebalance的危害?
Rebalance的在提升消费能力的同时,也带来一些问题:
a、消费暂停:在只有一个Consumer时,其负责消费所有队列;在新增了一个Consumer后会触发 Rebalance的发生。此时原Consumer就需要暂停部分队列的消费,等到这些队列分配给新的Consumer后,这些暂停消费的队列才能继续被消费。
b、消费重复:Consumer在消费新分配给自己的队列时,必须接着之前Consumer 提交的消费进度的offset 继续消费。然而默认情况下,offset是异步提交的,这个异步性导致提交到Broker的offset与Consumer实际消费的消息并不一致。这个不一致的差值就是可能会重复消费的消息。
c、消费突刺:由于Rebalance可能导致重复消费,如果需要重复消费的消息过多,或者因为Rebalance暂停时间过长从而导致积压了部分消息。那么有可能会导致在Rebalance结束之后瞬间需要消费很多消息。
五、Queue分配算法
一个Topic中的Queue只能由Consumer Group中的一个Consumer进行消费,而一个Consumer可以同时 消费多个Queue中的消息。那么Queue与Consumer间的配对关系是如何确定的,即Queue要分配给哪个Consumer进行消费,也是有算法策略的。常见的有四种策略,分别是:平均分配策略、环形平均策略、一致性hash策略、同机房策略。这些策略是通过在创建Consumer时的构造器传进去的。
(1)、平均分配策略(默认)
该算法是根据【avg = QueueCount / ConsumerCount 】的计算结果进行分配的,如果能够整除,则按顺序将avg个Queue逐个分配,如果不能整除,则将多余出的Queue按照Consumer顺序逐个分配。
(2)、环形分配策略
环形平均算法是指,根据消费者的顺序,依次由Queue队列组成的环形图逐个分配,该方法不需要提前计算。如下图:
(3)、一致性哈希分配策略
该算法会将consumer的hash值作为Node节点存放到hash环上,然后将queue的hash值也放到hash环 上,通过顺时针方向,距离queue最近的那个consumer就是该queue要分配的consumer。
一致性哈希算法可以有效减少由于消费者组扩容或缩容所带来的大量的Rebalance,所以它适合用在Consumer数量变化较频繁的场景 。如下图:
但是一致性哈希算法也存在不足,就是分配效率较低,容易导致分配不均的情况。即每个消费者消费的队列数,有可能相差很大,这样就会造成个别消费者压力过大。我们可以引入虚拟桶,让queue在hash环中尽可能分配均匀。
(4)、机房分配策略
该算法会根据queue的部署机房位置和consumer的位置,过滤出当前consumer相同机房的queue。然 后按照平均分配策略或环形平均策略对同机房queue进行分配。如果没有同机房queue,则按照平均 配策略或环形平均策略对所有queue进行分配。如下图: