RabbitMQ如何保证消息不会丢失?
可能会造成消息丢失的原因:
- 生产者方面,当生产者发送消息到 Broker 时,由于网络问题或者自身问题没有达到
- 消息到达Broker,但是当 Broker 持久化的时候,RabbitMQ宕机
- 消费者方面,消费者在接收消息时,出现宕机或者自身服务问题导致消息没收到
解决方法
- 生产端要做
confirm
机制, 当消息发送给 Broker 时,Broker都要告诉生产者。如果失败了,可以将失败的消息保存到数据库中,采用定期扫描的方式尝试重新推送。 - 消费端要做
手动ack
机制,当消费者消费成功之后,要告诉RabbitMQ删除队列中的消息。- 这样可能还存在一个问题,就是消费端返回
ack
时,突然宕机,由于消费者消费完了,但是队列中的消息还存在,就有可能造成重复消费
的问题
- 这样可能还存在一个问题,就是消费端返回
RabbitMQ 如何避免重复消费?
造成重复消费的原因:
生产者
:重复推送一条数据,比如:多次调用 Controller 接口,没有做接口幂等性导致MQ端
:消费者消费成功,响应 ack 时,MQ挂掉了,导致MQ认为还未消费这条数据,当MQ恢复后,继续推送这条数据导致重复消费消费端
:消费者消费完,正准备响应 ack 时,消费者挂掉了 ,消费者恢复之后,MQ仍然认为没有消费成功。
解决方法
- 使用数据库唯一约束
但是它局限性比较大,仅能在数据库新增
时生效,并且性能较低
- 在生产端将接口设计成
幂等性
, 防止重复推送同一条数据 - 使用防重表(Redis/MySQL), 插入消费记录
当消费者开始消费的时候,先在防重表
中插入一条记录,记录当前记录的状态为消费中
- 如果能插入成功,说明这条消息还没有消费过,去正常执行业务代码即可。
- 当业务代码执行失败时,将该条记录在
防重表
中删除,继续重试
- 当业务代码执行失败时,将该条记录在
- 如果插入失败,说明这条消息正在消费中或者已经消费过,但是由于某些原因导致未删除记录。
- 此时在判断这条消息是否已经消费过,消费过则返回成功结束,没有消费过,可以利用延时队列进行重试!
可以利用Redis
在防重表
中插入一条记录时,设置一个过期时间,为了避免在执行完业务代码、更新业务状态时出现异常而导致重复消费。
并且还可以记录一个 消费次数
的字段 , 当一条消息被多次消费 仍然不成功时,可以记录到日志中,由具体的开发人员分析到底是什么原因,具体分析!
RabbitMQ如何解决消息堆积问题?
产生消息堆积的原因:
-
消费者宕机积压
-
消费者消费能力不足积压
-
发送者发送流量太大
解决方法:
- 简化消费者的逻辑,减少耗时,提高消费速度
- 如果存在无法处理的消息,使用死信队列,防止堵塞
- 上线更多的消费者,进行正常消费