MQ高级
消息可靠性
说明:消息由生产者发送到MQ,最终被消费者正常消费挖不出。
RabbitMQ发送与消费消息的模型:
消息丢失的几种情况:
1.生产者连不上MQ:
2.生产者发送消息未达到交换机
3.MQ宕机,队列中的消息不见了
4.消费者收到消息,还没消费,消费者宕机了。
如何保证消息不丢失:
1.生产者确认机制
-
publisher-confirm
- 消息成功投递到交换机,返回ack
- 消息未成功投递到交换机,返回nack,记录消息以及交换机等相关信息到数据库,后期可以编写任务去补偿发送
-
publisher-return
- 未正确到达队列,返回ack及失败原因,记录消息以及交换机等相关信息到数据库,后期可以编写任务去补偿发送
2.持久化机制
- 交换机持久化
- 队列持久化
- 消息持久化
3.消费者ack机制
-
ack取值
-
none:只要消息到达消费者,消费者直接返回ack给MQ,MQ收到ack,会把队列中的消息删除,消息可能会丢失
-
manual:手动ack
- 消费成功,调用API给MQ返回ack
- 消费失败,调用API给MQ返回nack,并且让消息重回队列
-
auto:自动ack。消费消息不出异常,返回ack给MQ。消费消息出异常了,返回nack,把消息重回队列
-
本地重试
- 达到最大重试次数(可以配置)后,还是失败,则返回ack,不requeue。MQ会删除队列消息
-
失败策略
- RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息。默认就是这种方式
- ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队
- RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机
-
使用 RepublishMessageRecovere
- 把消息投递到失败的交换机,路由队列。记录日志,将来人工干预
-
-
消息重复消费问题
幂等性
说明:多次执行同一个操作,最终的结果是一样的。
对于非幂等性操作,多次消费消息,会造成数据一致性的问题,所以要保证重复消费消息的问题。
解决播放:
1、生产者
- 在发送消息时,向Redis写入一个消息的唯一ID(UUID),并且把它当作key写入到Redis
2、消费者
- 消费者先从Redis中获取消息ID,如果存在,则表示有人在消费消息,我什么都不做。如果不存在,表示是该消息是第一次被消费,正常消费,并且向存储Key为消息ID的数据到Redis,同时设置过期时间
-
消费者在消费消息时,先通过redis的setnx命令设置一个key为消息ID,值为0的操作。预先定义好0表示消息正在消费,1表示消息已经消费完成。
- 设置成功,表示当前消息没有被消费,消费消息,并且把redis的值改成1,手动ack
- 设置失败,表示消息正在被消费或者已经消费完成了,获取redis消息key的值,判断是否为1,如果为1,手动ack
消息积压问题
产生的原因:
-
生产者生产消息的速度 远高于 消费者消费消息的速度?于是就会造成消息积压在MQ
-
可能是设计问题,需要重新设置生产者与消费者数量匹配,比如有个生产者对应多个消费者
-
可能是消费者出了问题:
- 消费者出异常了,需要解决消费者代码
- 消费者宕机了,需要修复宕机的情况,并且临时开启多个消费者,来以倍速消费积压的消息。当积压的消息消费的差不多的情况,再关闭临时消费者
-
解决办法:
采用惰性队列:
- 提升消息的存储能力。
- 可以把消息持久化到本地,后面再读取慢慢消费
死信交换机
成为死信的几种情况:
- 消费者使用basic.reject 或 basic.nack 声明消费失败,并且消息的requeue参数设置为false
- 消息设置了过期时间,或者消息存放的队列设置了过期时间,超时无人消费
- 要投递的队列消息满了,无法投递
这些就是死信,然后会通过路由规则经过交换机路由到一个队列,这个交换机就叫死信交换机,这个队列就是死信队列
死信交换机:
如果这个包含死信的队列配置了 dead-letter-exchange 属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为 死信交换机 (Dead Letter Exchange,检查DLX)。
TTL:
超时未消费,消息变成死信的两种情况:
- 消息所在的队列设置了超时时间
- 消息本身设置了超时时间
如果两者都设置了,以短的时间为优先
延迟【时】队列:
- 一种实现 消费者延迟收到消息 的模式
- 在RabbitMQ中,没有延迟队列的功能。可以使用 TTL + 死信队列 的方式实现延迟队列
应用场景:
- 延迟发送短信
- 用户下单,如果用户在15 分钟内未支付,则自动取消订单
- 预约工作会议,20分钟后自动通知所有参会人员
DelayExchange插件:
因为延迟队列的需求非常多,所以RabbitMQ的官方也推出了一个插件,原生支持延迟队列效果
原理:
-
DelayExchange需要将一个交换机声明为delayed类型。
-
发送消息到delayExchange时的执行过程
- 接收消息
- 判断消息是否具备x-delay属性
- 如果有x-delay属性,说明是延迟消息,持久化到硬盘,读取x-delay值,作为延迟时间
- 返回routing not found结果给消息发送者
- x-delay时间到期后,重新投递消息到指定队列
RabbitMQ如何实现延迟队列:
- 死信队列 + TTL:由于队列的原因,会先消费队首的消息,如果后续的消息消费的时间比队首的要早,会导致消费无法消费。
- 装一个Delay插件:没有队列,不会出现无法消费的问题
RabbitMQ与Kafka的区别
Kafka
优点:
- 吞吐量大
- 功能强大:处理流媒体
缺点:
- 时效性差:消息延迟时间比MQ长
特点:
- 重Topic:收发消息都必须指定Topic
- 拉模式:由消费者主动去Broker中拉取消息
- 消息消费完后,不会删除,保留历史消息
RabbitMQ
优点:
- 时效性强、可靠性高
缺点:
- 吞吐量比Kafka小
特点:
- 轻Topic:不同工作模式可以选择是否使用Topic
- 推模式:由Broker主动把消息推送给消费者
- 消息阅后即焚