RabbitMQ如何保证消息不会丢失?

可能会造成消息丢失的原因

  • 生产者方面,当生产者发送消息到 Broker 时,由于网络问题或者自身问题没有达到
  • 消息到达Broker,但是当 Broker 持久化的时候,RabbitMQ宕机
  • 消费者方面,消费者在接收消息时,出现宕机或者自身服务问题导致消息没收到

解决方法

  • 生产端要做 confirm 机制, 当消息发送给 Broker 时,Broker都要告诉生产者。如果失败了,可以将失败的消息保存到数据库中,采用定期扫描的方式尝试重新推送。
  • 消费端要做 手动ack 机制,当消费者消费成功之后,要告诉RabbitMQ删除队列中的消息。
    • 这样可能还存在一个问题,就是消费端返回 ack 时,突然宕机,由于消费者消费完了,但是队列中的消息还存在,就有可能造成 重复消费 的问题

RabbitMQ 如何避免重复消费?

造成重复消费的原因

  • 生产者:重复推送一条数据,比如:多次调用 Controller 接口,没有做接口幂等性导致
  • MQ端:消费者消费成功,响应 ack 时,MQ挂掉了,导致MQ认为还未消费这条数据,当MQ恢复后,继续推送这条数据导致重复消费
  • 消费端:消费者消费完,正准备响应 ack 时,消费者挂掉了 ,消费者恢复之后,MQ仍然认为没有消费成功。

解决方法

  • 使用数据库唯一约束

但是它局限性比较大,仅能在数据库新增时生效,并且性能较低

  • 在生产端将接口设计成幂等性, 防止重复推送同一条数据
  • 使用防重表(Redis/MySQL), 插入消费记录

当消费者开始消费的时候,先在防重表 中插入一条记录,记录当前记录的状态为消费中

  • 如果能插入成功,说明这条消息还没有消费过,去正常执行业务代码即可。
    • 当业务代码执行失败时,将该条记录在 防重表 中删除,继续重试
  • 如果插入失败,说明这条消息正在消费中或者已经消费过,但是由于某些原因导致未删除记录。
    • 此时在判断这条消息是否已经消费过,消费过则返回成功结束,没有消费过,可以利用延时队列进行重试!

可以利用Redis防重表 中插入一条记录时,设置一个过期时间,为了避免在执行完业务代码、更新业务状态时出现异常而导致重复消费。

并且还可以记录一个 消费次数 的字段 , 当一条消息被多次消费 仍然不成功时,可以记录到日志中,由具体的开发人员分析到底是什么原因,具体分析!

RabbitMQ经典三问-LMLPHP

RabbitMQ如何解决消息堆积问题?

产生消息堆积的原因

  • 消费者宕机积压

  • 消费者消费能力不足积压

  • 发送者发送流量太大

解决方法

  • 简化消费者的逻辑,减少耗时,提高消费速度
  • 如果存在无法处理的消息,使用死信队列,防止堵塞
  • 上线更多的消费者,进行正常消费
04-11 08:22