生产环境MQ消息堆积的一般处理办法

前言

MQ,消息队列经常会用来解耦系统,由于前期的量预估不足,可能在某些时候导致MQ的消息堆积,导致业务异常,对于生产环境的业务,我们应该尽可能做到用户无感知地恢复服务。

正文

当我们的生产速率大于我们的消费速率的时候,就会表现出消息堆积,不同系统对消息堆积(处理延时)的容忍程度不同。

案例

假如现在有一个在线的业务,突然量大了起来,消费端或者说下游系统突然处理不过来了,MQ出现了大量的消息堆积,业务写入MQ异常,有什么办法可以相对平缓得解决呢?

解决思路

  1. 从生产者端解决

    一般我们的系统容量或者处理能力都是规划好的,出现消息堆积的情况,大部分是由于流量暴增引起,这个时候可以考虑控制生产者的速率,对前端机器流量进行限速限流。

  2. 从消费者端解决。

    消费者端解决的思路有两种

    • 假如消费者数还有增加的空间,那么我们加消费者解决。
    • 假如没有拓展的可能,但吞吐量还没达到MQ的上限,只是消费者消费能力不足,比如消费者总体消费能力已经到达上线(数据库写入能力等),或者类似Kafka的消费者数量与partition数有关,如果前期设计没有做好水平拓展的设计,这个时候多少个partition就只能对应多少个消费者。这个时候我们可以先把一部分消息先打到另外一个MQ中或者先落到日志文件中,再拓展消费者进行消费,优先恢复上游业务
  3. 从整理系统上进行解决。

    第2点有提到就是有些MQ的设计限制,导致的消费者数是没法动态拓展的,这个时候可以考虑将原先队列进行拆分,比如新建一个topic 分担一部分消息,这个方式需要对系统的上下游都要进行调整,在实际操作难度可能比较高,处理起来可能也比较耗时,如果在事前有做好这个设计那事发后就能很好进行调整。

补充

  1. 如果采用先将消息消费到日志文件的方式,怎么保证时序性?

    一般消息队列都有时序问题,我们需要根据业务,对消息进行分区路由,比如根据用户纬度,只保证同一个用户的消息时序就行了,比如我把id为1~10000的用户写在一个文件中,10001~20000的写在一个文件中。后面按这个文件单独消费就能保证消息的时序。

  2. 不同MQ的消息堆积能力

    其实主要是看是采用内存堆积还是磁盘堆积了,一般像kafka磁盘堆积的,堆积能力都是很强的不会出现容量不足的写入异常,而像RabbitMQ这种内存堆积的,如果消费者跟不上,很容易就把内存堆满了。

总结

其实上面一对废话,最终总结下来就是,出现堆积后防止业务出现写入异常,需要把消息队列清出一份容量出来,也就是保证消息时序的情况下先将消息快速消费到一些速度更快的存储上,事后再写程序处理。