处理积压消息

一、避免消息积压

对于绝大多数使用消息队列的业务来说,消息队列本身的处理能力要远大于业务系统的处理能力。主流消息队列的单个节点,消息收发的性能可以达到每秒钟处理几万至几十万条消息的水平,还可以通过水平扩展 Broker 的实例数成倍地提升处理能力。而一般的业务系统需要处理的业务逻辑远比消息队列要复杂,单个节点每秒钟可以处理几百到几千次请求,已经可以算是性能非常好的了。所以,对于消息队列的性能优化,我们更关注的是,

在消息的收发两端,我们的业务代码怎么和消息队列配合,达到一个最佳的性能。

1.发送端性能优化

  • 如果说,你的代码发送消息的性能上不去,你需要优先检查一下,是不是发消息之前的业务逻辑耗时太多导致的。

  • 无论是增加每次发送消息的批量大小,还是增加并发,都能成倍地提升发送性能。至于到底是选择批量发送还是增加并发,主要取决于发送端程序的业务性质。简单来说,只要能够满足你的性能要求,怎么实现方便就怎么实现。

1.1增加并发

如果你消息发送端是一个微服务,主要接受 RPC 请求处理在线业务。很自然的,微服务在处理每次请求的时候,就在当前线程直接发送消息就可以了,因为所有 RPC 框架都是多线程支持多并发的,自然也就实现了并行发送消息。并且在线业务比较在意的是请求响应时延,选择批量发送必然会影响 RPC 服务的时延。这种情况,比较明智的方式就是通过并发来提升发送性能。

1.2批量发送

如果你的系统是一个离线分析系统,离线系统在性能上的需求是什么呢?它不关心时延,更注重整个系统的吞吐量。发送端的数据都是来自于数据库,这种情况就更适合批量发送,你可以批量从数据库读取数据,然后批量来发送消息,同样用少量的并发就可以获得非常高的吞吐量。

2.消费端性能优化

2.1一般情况

如果消费的速度跟不上发送端生产消息的速度,就会造成消息积压。如果这种性能倒挂的问题只是暂时的,那问题不大,只要消费端的性能恢复之后,超过发送端的性能,那积压的消息是可以逐渐被消化掉的。

2.2潜在故障

要是消费速度一直比生产速度慢,时间长了,整个系统就会出现问题,要么,消息队列的存储被填满无法提供服务,要么消息丢失,这对于整个系统来说都是严重故障。

  • 一定要保证消费端的消费性能要高于生产端的发送性能,这样的系统才能健康的持续运行。

优化方式:

  • 优化消费业务逻辑
  • 水平扩展,增加消费端的并发数来提升总体的消费性能。
    在扩容 Consumer 的实例数量的同时,必须同步扩容主题中的分区(也叫队列)数量,确保 Consumer 的实例数和分区数量是相等的。

如果 Consumer 的实例数量超过分区数量,这样的扩容实际上是没有效果的。原因我们之前讲过,因为对于消费者来说,在每个分区上实际上只能支持单线程消费。

3.傻子反例

在onMessage中实现一个内存队列


image.png

它收消息处理的业务逻辑可能比较慢,也很难再优化了,为了避免消息积压,在收到消息的 OnMessage 方法中,不处理任何业务逻辑,把这个消息放到一个内存队列里面就返回了。然后它可以启动很多的业务线程,这些业务线程里面是真正处理消息的业务逻辑,这些线程从内存队列里取消息处理,这样它就解决了单个 Consumer 不能并行消费的问题。这个方法是不是很完美地实现了并发消费?请注意,这是一个非常常见的错误方法! 为什么错误?因为会丢消息。如果收消息的节点发生宕机,在内存队列中还没来及处理的这些消息就会丢失。

二、消息积压如何处理

消息积压的情况是,日常系统正常运转的时候,没有积压或者只有少量积压很快就
消费掉了,但是某一个时刻,突然就开始积压消息并且积压持续上涨。这种情况下需要你在
短时间内找到消息积压的原因,迅速解决问题才不至于影响业务。

1.排查消息积压的原因,思考方向(粗粒度)

  • 发送变快了
  • 消费变慢了
    大部分消息队列都内置了监控的功能,只要通过监控数据,很容易确定是哪种原因。

1.1 发送变快了

  • 唯一的方法是通过扩容消费端的实例数来提升总体的消费能力。
  • 如果短时间内没有足够的服务器资源进行扩容,没办法的办法是,将系统降级,通过关闭一
    些不重要的业务,减少发送方发送的数据量,最低限度让系统还能正常运转,服务一些重要
    业务。

1.2 不常见情况--消息重试失败--多次重试

  • 你通过监控发现,无论是发送消息的速度还是消费消息的速度和
    原来都没什么变化,这时候你需要检查一下你的消费端,是不是消费失败导致的一条消息反
    复消费这种情况比较多,这种情况也会拖慢整个系统的消费速度。
  • 如果监控到消费变慢了,你需要检查你的消费实例,分析一下是什么原因导致消费变慢。优
    先检查一下日志是否有大量的消费错误,如果没有错误的话,可以通过打印堆栈信息,看一
    下你的消费线程是不是卡在什么地方不动了,比如触发了死锁或者卡在等待某些资源上了。

你可能感兴趣的:(处理积压消息)