RabbitMQ面试
消息堆积
当消息生成的速度远远大于消费的速度时,就会造成消息堆积;
消息堆积的影响:
- 可能导致新消息无法进入队列;
- 可能导致旧消息无法丢失;
- 消息等待消费的使劲过长,超出业务容忍范围;
产生消息堆积的情况:
- 生产者突然大量发布消息;
- 消费者消费失败;
- 消费者出现性能瓶颈;
- 消费者挂掉;
解决方法:
- 排查消费者的消费性能瓶颈;
- 增加消费者的多线程处理能力;
- 增加多个消费者;
消息丢失
在实际的生产环境中有可能出现一条消息因为某些原因丢失,导致消息没有消费成功,从而造成数据不一致等问题,造成严重后果;
消息丢失的场景分为:消息在生产者丢失、消息在RabbitMQ丢失、消息在消费者丢失;
消息在生产者丢失
场景介绍:
消息生产者发送消息成功,但是 MQ 没有收到消息,消息在从生产者传输到 MQ 的过程中丢失,一般是由于网络不稳定的原因;
解决方案:
采用 MQ 发送消息确认机制,当消息成功被 MQ 接收到时,会给生产者发送一个确认信息,表示成功接收到投递的消息;MQ 发送方消息确认模式有三种:普通确认模式、批量确认模式、异步监听确认模式;Spring 整合 RabbitMQ 后只使用异步监听确认模式;
说明:异步监听模式,可以实现边发送边进行确认,不影响主线程任务的执行;
消息在RabbitMQ丢失
场景介绍:
消息成功发送到 MQ,消息还没有到消费者却在 MQ 中丢失,比如 MQ 服务器宕机或者重启会出现这种情况
解决方案:
持久化交换机、队列、消息;确保 MQ 服务器重启时依然能从磁盘恢复对应的交换机、队列、消息;
Spring 整合后默认开启了交换机、队列、消息的持久化,所以不修改任何配置情况下,可以确保消息不在 MQ 丢失。
消息在消费者丢失
场景介绍:
消费者消费消息时,如果设置为自动回复 MQ,消费者端接收到消息会自动回复 MQ 服务器,MQ 则会删除该消息,如果消息已经在 MQ 中删除,但是消费者的业务处理出现异常或者消费者服务宕机,那么就会导致该消息没有处理成功从而导致该条消息丢失;
解决方案:
设置为收到回复 MQ 服务器,当消费者出现异常或者服务宕机时,MQ 服务器不会删除该消息,而是会把消息重复给绑定该队列的消费者,如果该队列只绑定了一个消费者,那么该消息会一直保存在MQ服务器中,直到消费者能正常消费为止。
MQ 重发消息场景:
- 消费者未响应 ACK,主动关闭 channel 或者 connection ;
- 消费者未响应 ACK,消费者服务宕机;
有序消费消息
乱序消费消息场景:
- 场景1:当 RabbitMQ 采用
work queue
模式,此时只会有一个 Queue,但是会有多个消费者,同时多个消费者是竞争关系,此时就会出现消息乱序消费问题; - 场景2:当 RabbitMQ 采用简单队列的时候,如果消费者采用多线程的方式来加速消息的处理,此时也会出现消息乱序消费问题;
场景一解决方案:
生产者根据某种规则,求出结果相同的消息只投递给同一队列,队列只绑定一个消费者;
场景二解决方案:
消费者拉去消息时,根据某种规则算出结果,相同结果的消息压到同一内存队列中,让同一线程处理;
重复消费
场景介绍:
为了防止消息在消费者丢失,会采用收到回复MQ的方式来解决,但是也同时引出了一个问题,消费者处理消息成功,手动回复MQ时由于网络不稳定,连接断开,导致 MQ 没有收到消费者回复的消息,那么该条消息还会保存在 MQ 消息队列中,由于 MQ 的重发机制,会把该消息重新发送给消费者,这样会导致重复消费。而有些操作是不允许重复消费的,比如下单、减库存、扣款等
MQ 重发消息场景:
- 消费者未响应 ACK,主动关闭 channel 或者 connection ;
- 消费者未响应 ACK,消费者服务宕机;
解决方案:
如果消费者消息的业务是幂等性操作(同一个操作执行多次,结果不变)就算重复消费也没有问题,可以不做处理;
如果不支持幂等性操作,如:下单、减库存、扣款等,那么可以在消费者端每次消费成功后将该条消息id保存到数据库中,每次消费前查询该消息id,如果该消息id已经存在那么表示已经消费过就不再消费,否则消费。方法可以采用 redis 存储消息 id