中间件系列之RabbitMQ-7-其他典型问题分析

610 阅读6分钟

1.消息堆积

在长时间内,当消息生产的速度远远大于消息消费的速度,就会造成消息堆积。

消息堆积的影响:

  • 可能导致新消息无法进入队列
  • 可能导致旧消息无法丢失
  • 消息等待消费的时间过长,超出了业务容忍范围

产生堆积的情况:

  • 生产者突然大量发布消息
  • 消费者消费失败
  • 消费者出现性能瓶颈

解决思路:

  • 排查消费者消费性能瓶颈
  • 增加消费者多线程处理
  • 部署增加多个消费者

如果消息已经堆积,又该如何解决呢?一个典型的思路,如果业务允许,想办法把已经堆积的消息转移到一个新的队列,使得原队列能继续对外服务,增加服务器来消费这个新的队列即可。

2.有序消费消息

当RabbitMQ存在多个消费者Consumer时,显然的,多个Consumer是竞争关系,此时就会出现MQ消息乱序的问题。

如果所示,理情况下,我们想让消息与对应的消费者一一顺序对应,但实际情况的顺序很可能是不一致的。

此外,还有另一种情况,消费者只有一个,但是利用了多线程消费消息,也会造成顺序不一致的情况

针对以上两种情况,考虑有以下解决方案:

  • 针对多个消费者消费

    考虑增加队列,采用一定的路由算法,使得消息与对应的队列绑定,队列又与对应的消费者绑定。比如如下所示,利用ID对队列个数取余,就能让相同ID的消息发送到用一个队列,一个队列由一个消费者消费,不存在消费者竞争的情况。

  • 针对单个消费者,多线程消费

    与多消费的解决思路类似,可以利用内存队列,比如Java的Queue,将消息进行分组,然后每一个分组后的消息交由一个线程处理

    总之,整体思路就是,只要保证入队有序就行,出队以后的顺序交给消费者自己去保证,没有固定套路。

3.重复消费

其实无论是那种消息队列,造成重复消费原因其实都是类似的。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。那造成重复消费的原因?,就是因为网络传输等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。   如何解决?这个问题针对业务场景来答分以下几点   (1)比如,你拿到这个消息做数据库的insert操作。很容易就想到,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。   (2)再比如,你拿到这个消息做redis的set的操作,都不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。   (3)如果上面两种情况还不行,准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

4.高可用

普通集群

多个联通的服务器上安装不同的RabbitMQ的服务,这些服务器上的RabbitMQ服务组成一个个节点,通过RabbitMQ内部提供的命令或者配置来构建集群,形成了RabbitMQ的普通集群模式。

  • 当用户向服务注册一个队列,该queue会随机保存到某一个服务节点上,然后将对应的元数据(即queue的标识信息)同步到各个不同的服务节点上
  • RabbitMQ的普通集群模式中,每个RabbitMQ都保存有相同的元数据
  • 用户只需要链接到任一一个服务节点中,就可以监听消费到对应队列上的消息数据
  • 但是RabbitMQ的实际数据却不是保存在每个RabbitMQ的服务节点中,这就意味着用户可能联系的是RabbitMQ服务节点C,但是C上并没有对应的实际数据,也就是说RabbitMQ服务节点C,并不能提供消息供用户来消费,那么RabbitMQ的普通集群模式如何解决这个问题呢?
  • RabbitMQ服务节点C发现自己本服务节点并没有对应的实际数据后,因为每个服务节点上都会保存相同的元数据,所以服务节点C会根据元数据,向服务节点B(该服务节点上有实际数据可供消费)请求实际数据,然后提供给用户进行消费
  • 这样给用户的感觉就是,在RabbitMQ的普通集群模式中,用户连接任一服务节点都可以消费到消息 -普通集群模式的优点:提高消费的吞吐量

普通集群模式的原理比较简单,但是并不能真正意义上的实现高可用,他也存在以下的以下缺点:

  1. 为了请求RabbitMQ的实际数据以提供给用户,可能会在RabbitMQ内部服务节点之间进行频繁的进行数据交互,这样的交互比较耗费资源
  2. 当其中一个RabbitMQ的服务节点宕机了,那么该节点上的实际数据就会丢失,用户再次请求时,就会请求不到数据,系统的功能就会出现异常

镜像集群

与普通集群的区别:

  • 生产者向任一服务节点注册队列,该队列相关信息会同步到其他节点上
  • 任一消费者向任一节点请求消费,可以直接获取到消费的消息,因为每个节点上都有相同的实际数据
  • 任一节点宕机,不影响消息在其他节点上进行消费

但是其本身也有相应的缺点:

  1. 性能开销非常大,因为要同步消息到对应的节点,这个会造成网络之间的数据量的频繁交互,对于网络带宽的消耗和压力都是比较重的
  2. 没有扩展可言,rabbitMQ是集群,不是分布式的,所以当某个Queue负载过重,我们并不能通过新增节点来缓解压力,因为所以节点上的数据都是相同的,这样就没办法进行扩展了

搭建镜像

RabbitMQ集群搭建