生产环境mq消息堆积百万的血泪教训

255 阅读4分钟

背景

业务服务有一个流量消耗的场景,业务每产生一条流量消耗,就会发送一条mq,而我负责的服务,则会消费这条消息,做对应的业务处理。

  • mq:Rocket-MQ
  • mq版本;5.0
  • mq云平台:aliyun Saas版 标准版
  • topic类型:普通消息
  • consumer-group投递顺序性:顺序投递
  • 客户端版本:5.0
  • 平均生产速率:200/s
  • 平均堆积条数:8
  • 消费最大重试次数:16

事故描述

由于是新上线的功能,还没有加监控通知,我便像往常一样,查看mq的实例仪表盘,查看各项指标。可当我看到自己负责的consumer-group在消费场景指标-消息堆积量(条)一栏,达到了113w,一下子脑袋翁的炸了,便赶紧去查看sls中的error日志和容器日志,发现全是报错的堆栈信息。

慌乱出错

此时心里慌得一匹,但没有马上做决定实施和处理,先去了一趟厕所(之前遇到问题和bug容易卡住,后来在《代码大全》里学到一招,先不做决定,走一走或者上个厕所,心态和思路都会发生变化,情绪也会稳定下来,亲测有效),然后再回来坐下。

一看消息堆积量如此之大,肯定要先找到报错的原因,根据堆栈并结合业务发现,分析原因:

  • 1号凌晨,收到了上月30号的消息,跨月消费时,会进行校验,不通过,则会抛出异常。
  • 一部分数据,需要先做初始化,才能正常消费后面的数据
  • 一部分有时限性的其他类型消息,比如3号的消息只能3号消费,4号进来便会报错。

顺序投递模式下消费者只能按照消息分组的顺序获取消息,前面的消息没有提交完成则无法获取消费后续的消息

根据我们项目的配置,加上阅读阿里云的相关说明,确定了堆积的原因,便开始着手想解决办法。

思考之后,我有了两个选择,

  • 一是丢弃这部分消息
  • 二是加快消费,争取达到16次之后,自动丢弃

由于当时环境+时间,怕把相关代码改错,加上没有测试资源,于是选择了后者,通过逐步增加消费者pod数量,增加几次后,达到了20个,消费速率,也达到了4.7w/s,同时,为了加快消费速度,便将consumer-group投递顺序性改为了「并发投递」(这一选择,真的是一大败笔)。原本想着,并发投递,可以增加消费速度,可以更快的处理完挤压的消息,可却还是因为慌乱+无知,酿成大祸。

由于这个consumer-group配置为顺序投递的原因,就是因为原因2的存在。可由于改成了并发消费,导致原有的消费顺序不复存在,导致产生了大量的消息丢弃,这下,我这只蝴蝶扇了一下翅膀,导致整个太平洋都为之一振了。在发现问题时,赶紧改为了顺序投递。

配置是改回来了,可这段时间产生的问题数据要去修正啊,就只有等消费结束,去业务库捞数据回来补了

等到消费完毕,将pod数量改回原有值,同时也开始了漫长的补数据之旅

有了上面的教训,补数据的时候,谨慎值增加了100分

正确操作

由于已经有一部分数据是确定丢失了,肯定是需要手动补数据,可由于顺序投递的特性,一条报错,让后面的消息乖乖排队,便可以利用这一特性

  • 消费时手动丢弃这部分无法处理的消息
  • 增加pod提高消费速率
  • 补数据

总结

  • 遇事莫慌,冷静分析问题原因,不要放过任何一个

就像原因2,就是因为在改配置时,没有考虑到,从而造成衍生问题

  • 消息堆积不可怕,可怕的是误操作,扩大「熵」
  • 不要妄想一个操作,把产生的所有问题消息全都处理了
  • mq进行消费时,考虑下个方面配置,完善补偿处理机制,避免消息堆积

这次的堆积,如果在编码时,考虑到产生的三个原因,其实是完全可以避免的