解决方案
- 如果消费者数量小于Topic的分区数(RocketMq的Topic队列数),那么增大消费者数量直到分区数,这是可以提高消费速度。否则,就没有效果。
增加消费者数量一定会提高消费速度吗?(小于分区数)
也不一定,消费者消息拉取的速度也取决于本地消息的消费速度,如果本地消息消费的慢,就会延迟一段时间后再去拉取。
消费者拉取的消息存在 ProcessQueue,消费者是有流量控制的,如果出现下面三种情况,就不会主动去拉取:
- ProcessQueue 保存的消息数量超过阈值(默认 1000,可以配置);
- ProcessQueue 保存的消息大小超过阈值(默认 100M,可以配置);
- 对于非顺序消费的场景,ProcessQueue 中保存的最后一条和第一条消息偏移量之差超过阈值(默认 2000,可以配置)。
对于顺序消费的场景,ProcessQueue 加锁失败,也会延迟拉取,这个延迟时间是 3s。
其实延迟拉取的本质就是消费者消费慢,导致下次去拉取的时候 ProcessQueue 中积压的消息超过阈值。
消费者消费慢,可是能下面的原因:
- 消费者处理的业务逻辑复杂,耗时很长;
- 消费者有慢查询,或者数据库负载高导致响应慢;
- 缓存等中间件响应慢,比如 Redis 响应慢;
- 调用外部服务接口响应慢。
RocketMq的消费负载策略
平均负载策略 :
- 把消费者进行排序;
- 计算每个消费者可以平均分配的 MessageQueue 数量;
- 如果消费者数量大于 MessageQueue 数量,多出的消费者就分不到;
- 如果不可以平分,就使用 MessageQueue 总数量对消费者数量求余数 mod;
- 对前 mod 数量消费者,每个消费者加一个,这样就获取到了每个消费者分配的 MessageQueue 数量。
循环分配策略 :
这个很容易理解,遍历消费者,把 MessageQueue 分一个给遍历到的消费者,如果 MessageQueue 数量比消费者多,需要进行多次遍历,遍历次数等于 (MessageQueue 数量/消费者数量)。
自定义分配策略 :
这种策略在消费者启动的时候可以指定消费哪些 MessageQueue
按照机房分配策略 :
这种方式 Consumer 只消费指定机房的 MessageQueue,如下图:Consumer0、Consumer1、Consumer2 绑定 room1 和 room2 这两个机房,而 room3 这个机房没有消费者
按照机房就近分配 :
跟按照机房分配原则相比,就近分配的好处是可以对没有消费者的机房进行分配。如下图,机房 3 的 MessageQueue 也分配到了消费者。
一致性 Hash 算法策略 :
把所有的消费者经过 Hash 计算分布到 Hash 环上,对所有的 MessageQueue 进行 Hash 计算,找到顺时针方向最近的消费者节点进行绑定