大家好,我是苍何。

上篇文章 [[CodeCanvas/rocketmq/图解RocketMQ之消息模型详解(1).md]] 我们理解了消息队列的两大基础模型以及搞明白了 RocketMQ 中的消息位点消费位点

图解RocketMQ之消息模型详解(2)_指定位置

上一篇留了 2 个问题:

  • 当有大量消息堆积的时候,RocketMQ 如何处理消息堆积?
  • 如何实现重复消费或者跳过部分消息不消费?

要想回答这两个问题,其实就要涉及到 RocketMQ 的消费进度管理,上一篇中两个位点是消费进度的核心,那么我们这一篇就带着这 2 个问题来继续深入了解 RocketMQ 的消费进度。

掌握好,至少在面试层面,可以拷打面试官了🐶

RocketMQ 如何处理消息堆积

要知道如何处理堆积,我们先得了解下什么是消息堆积?

就像是鸡毛餐厅的后厨堆了一大堆没有洗的碗一样,消息在队列中不及时消费掉也会形成堆积。

那消息堆积是如何产生的呢?如果是厨房堆积的碗你可能很好想到,无非就是洗碗的阿姨不够以及顾客太多。

图解RocketMQ之消息模型详解(2)_重置_02

消息堆积主要由以下几点原因:

  1. 消费者处理能力不足:
  • 消费者数量太少
  • 单个消费者处理速度慢
  • 消费者出现异常或宕机
  1. 生产者突发流量:
  • 短时间内大量消息涌入
  • 定时任务集中发送消息
  1. 消息处理逻辑复杂:
  • 消息处理涉及复杂计算或I/O操作
  • 消息处理依赖的外部系统响应慢
  1. 网络问题:
  • 网络延迟或不稳定
  • 消费者与broker之间的网络拥塞
  1. 硬件资源不足:
  • 消费者所在服务器CPU、内存等资源不足
  • 磁盘I/O成为瓶颈

那处理消息堆积其实也有很多办法,如优化消费逻辑、调整消费者配置、提高硬件配置等,后面在面试篇会单独更详细的说明,那今天的主题其实是消息模型,所以我们能想到的办法应该是:

  • 增加队列
  • 增加消费者
  • 重置消费位点

这也是最直接干脆的方式,就像增加几个洗碗阿姨和洗碗池是最直接的解决鸡毛餐厅脏碗堆积的问题。

图解RocketMQ之消息模型详解(2)_重置_03

之前鸡毛和狗毛进行消费,可以看到他两倒是不用喘气就干完了。

图解RocketMQ之消息模型详解(2)_指定位置_04

随着事情(消息)越来越多,这两人根本干不完,每天加班加点也不得行啊,两人欲哭无泪。

图解RocketMQ之消息模型详解(2)_重置_05

在鸡毛和狗毛的苦苦哀求下,老板终于给他两各配了个小助理(他们觉得是老板突然有了良心,真实原因不得而知),而且还给大家提高了配置,增加了多台电脑,你可以理解成是加了队列。

图解RocketMQ之消息模型详解(2)_点更新_06

所以,解决消息堆积的最直接的方式就是增加消费者和增加队列,而这两者基本是一块操作的,增加了多少消费者理应对应增加多少队列,至少保证:

队列的数量 >= 消费者的数量

增加队列,仅需要修改对应的主题即可,所以 RocketMQ 这套设计不得不让人佩服,还是很灵活的,不同的消费者之间有自己的消费记录(消费位点),互不干扰,保证消息能被很好的进行消费处理。

另外一种暴力的做法就是重置消费位点。可以重置消费位点快速将消费位点更新到指定位置,绕过这部分堆积的消息,减少下游处理压力。

但这个的前提是堆积的消息可以丢弃,如果消息不能丢弃,最好别用这个方法,否则你概率会被领导吊一顿。🐶

处理消息堆积是没问题了,那问题很多的小明他又问了,消费过的消息我想再次消费或者有些消息我想跳过不消费,该怎么办呢?

要解决这个问题其实很简单,还是用到重置消费位点,下面我们具体看看这是个什么玩意儿。

重置消费位点

这是 RocketMQ 官方提供的消费进度管理能力。核心就是可以更改消费者的消费位点到想要的位置。

主要有以下作用:

  • 重置到队列中的指定位点。
  • 重置到某一时刻对应的消费位点,匹配位点时,服务端会根据自动匹配到该时刻最接近的消费位点。

比如鸡毛现在消费的是 vip-topic-队列 2-M 7 的消息,但还没消费完网络就断了,好巧不巧,服务端上的 RocketMQ 也宕了,等双方都重启,鸡毛还是想继续消费 vip-topic-队列 2-M 7 的消息,那就可以将消费位点设置到队列 2-M 7 。

这样其实就解决了上面提到的 2 个问题,重复消费消息和跳过不消费的消息,操作起来也很简单,仅仅需要重置下消费位点。

图解RocketMQ之消息模型详解(2)_指定位置_07

操作上也比较简单,直接在控制台点击重置消费位点即可,

图解RocketMQ之消息模型详解(2)_rocketmq_08

当然也可以使用 mqadmin 命令行工具:

mqadmin updateSubGroup -n <nameserver地址> -b <broker名称> -g <消费组名> -s <重置类型> -t <重置时间戳>
  • 1.

重置类型(-s)可以是:

  • timestamp: 按时间戳重置
  • earliest: 重置到最早的消息
  • latest: 重置到最新的消息

最后,还可以使用 Java 来重置,可以使用RocketMQ的AdminTools API可以编程式重置消费位点:

DefaultMQAdminExt admin = new DefaultMQAdminExt();
admin.setNamesrvAddr("nameserver地址");
admin.start();

// 按时间戳重置
admin.resetOffsetByTimestamp("主题", "消费组名", timestamp, true);

// 重置到最早的消息
admin.resetOffsetToEarliest("主题", "消费组名");

// 重置到最新的消息
admin.resetOffsetToLatest("主题", "消费组名");

admin.shutdown();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

重置消费位点总结有以下适用场景,小伙伴们可以根据情况自行选择哦(哦不,最主要的还是可以和面试官吹水):

  • 初始消费位点不符合需求:因初始消费位点为当前队列的最大消息位点,即客户端会直接从最新消息开始消费。若业务上线时需要消费部分历史消息,可以通过重置消费位点功能消费到指定时刻前的消息。
  • 消费堆积快速清理:当下游消费系统性能不足或消费速度小于生产速度时,会产生大量堆积消息。若这部分堆积消息可以丢弃,可以通过重置消费位点快速将消费位点更新到指定位置,绕过这部分堆积的消息,减少下游处理压力。
  • 业务回溯,纠正处理:由于业务消费逻辑出现异常,消息被错误处理。若希望重新消费这些已被处理的消息,可以通过重置消费位点快速将消费位点更新到历史指定位置,实现消费回溯。

总结

我们知道了重置消费位点可以解决消息堆积、重复消费历史消息、跳过消费某些消息等功能,可谓是收获满满呀。

但这功能虽好用,也是带着很大风险的,特别是回溯重置类场景,可能会造成系统压力上升,因此也不是想重置就立马重置的,生产上需要严格控制好权限,做好评估

最后还有一点需要我们注意的是,RocketMQ 中重置消费位点只能重置对消费者可见的消息,不能重置定时中、重试等待中的消息,这部分消息如何处理我们后面也会介绍。

好啦,本篇文章就到这里,我是苍何,这是图解 RocketMQ 教程的第 4 篇,我们下篇见~