消息积压

其实对于一个原本正常的消息系统来说消息积压,只会出现两种情况:

  • 要么生产者消息数量增加导致的积压;
  • 要么就是消费者消费变慢导致的消息积压。

对于一个消息队列我们肯定在上线前就预估好,单节点最大承受流量与系统目前最大峰值流量的数据,一般情况下消息队列收发性能是远大于业务处理性能的,一旦出现的话问题也很显而易见:

  • 要么就是流量突然增加,
  • 要么就是业务逻辑异常。

生产端
一般当生产端发生积压(Broker正常的情况下)就要查看你的业务逻辑是否有异常的耗时步骤导致的。是否需要改并行化操作等。
Broker端
当Broker端发生积压我们首先要查看,消息队列内存使用情况,如果有分区的的话还得看每个分区积压的消息数量差异。

  • 当每个分区的消息积压数据量相对均匀的话,我们大致可以认为是流量激增需要在消费端做优化,或者同时需要增加Broker节点(相当于存储扩容)
  • 如果分区加压消息数量差异很大的话(有的队列满了,有的队列可能还是空闲状态)需要检查路由转发规则是否合理

消费端
在使用消息队列的时候大部分的问题都出在消费端,当消费速度小于生产速度很快就会出现积压,导致消息延迟,以至于丢失。这里需要重点说明一点的是,当消费速度小于生产速度的时候,仅增加消费者是没有用处的,因为多个消费者在同一个分区上实际是单线程资源竞争关系(当然还有一些冒险的单队列多消费者并行方式就是:消费者接到消息就ack成功再去处理业务逻辑,这样你就要承受消息丢失的代价),需要同时增加Broker上的分区数量才能解决这一问题

  • 需要检查是否有无限重发的消息或者有进入死锁的程序等等,
  • 当确定是流量激增的话,我们需要评估是否需要增加资源还是通过限流的方式解决,
  • 当短时间大量消息需要处理时,在资源允许的情况下,可以新启一批消费者与消息队列,将原来的消费者中的消息直接作为生产者转发到临时应急队列中,这样大概率的能够快速解决消息积压。

与其事后处理不如我们在设计之初就要把积压考虑进来,对于数据量非常大,但是实时性要求不高的场景,可以设计出批量消息发送,当队列积累到一定阀值再做批量消费消费,这里需要注意的就是重复消费带来的影响,设计不好就是一场灾难。

防止消息积压的解决思路:

  1. 拆分MQ——生产者一个MQ,消费者一个MQ,写一个程序监听生产者的MQ模拟消费速度(譬如线程休眠),然后发送到消费者的MQ,如果消息积压则只需要处理生产者的MQ的积压消息,不影响消费者MQ
  2. kafka 消息积压问题 kafka消息积压的影响_kafka 消息积压问题

  3. 拆分MQ——生产者一个MQ,消费者一个MQ,写一个程序监听生产者的MQ,定义一个全局静态变量记录上一次消费的时间,如果上一次时间和当前时间之差小于消费者的处理时间,则发送到一个延迟队列(可以使用死信队列实现)发送到消费者的MQ,如果消息积压则只需要处理生产者的MQ的积压消息,不影响消费者MQ
  4. kafka 消息积压问题 kafka消息积压的影响_业务逻辑_02

  5. 使用Redis的List或ZSET做接收消息缓存,写一个程序按照消费者处理时间定时从Redis取消息发送到MQ
  6. kafka 消息积压问题 kafka消息积压的影响_kafka 消息积压问题_03

  7. 设置消息过期时间,过期后转入死信队列,写一个程序处理死信消息(重新如队列或者即使处理或记录到数据库延后处理)
  8. kafka 消息积压问题 kafka消息积压的影响_MQ_04