MQ 常见的问题
消息的可靠性问题(如何确保发送的消息至少被消费一次)
生产者确认机制
-
publisher-confirm 发送者确认
消息成功的发送到交换机 返回ack
消息没有发送到交换机返回 nack -
publisher -return 发送者回执
-
消息发送到了交换机但是没有路由到队列,返回ack以及路由失败的原因
确认机制发送消息的时候要给每一个消息设置一个id区分不同的消息避免ack的冲突
MQ默认是内存存储消息开启持久化可以保证缓存在mq的消息不丢失
1交换机持久化
2队列持久化
3消息持久化springamqo中的消息默认是持久的通过messageproperties的deliveryMode来指定
消费者确认
消费者处理完消息之后可以向mq发送ack回执 mq收到ack回执之后才会删除消息soringamqp允许配置三种模式
- manual:手动ack 需要在业务代码结束后调用api发送ack
- auto:自动ack 由spring检测listener代码是否出现异常没异常返回ack抛出异常返回nack
- none:关闭ack mq假定消费者获取消息后会成功处理消息投递之后立即删除
消息失败重试机制
消费者出现异常之后消息会不断的requeue到队列在发送给消费者,然后在次异常无限循环。导致mq的消息处理能力飙升 带来不必要的压力
我们可以送spring的retry机制在消费者抛异常利用本地重试而不是无限制的requeue到mq队列
消费者发送消息失败的处理策略
开启重试模式之后重试的次数消耗殆尽消息发送失败的话需要有messsageRecover接口来处理 有三种实现
如何确保rabbitmq消息的可靠性
- 开启了生产者确认机制确保消息可以到达队列
- 开启持久化功能确保生产者的消息在队列中不会消失
- 开启消费者确认机制为auto由spring确认消息处理之后完成ack
- 开启消费者失败重试机制 设置messagerecover对此重试失败之后消息投递到异常交换机交给人工处理
延迟消息问题(如何实现消息的延迟投递)
死信交换机
满足以下条件的称为死信
- 消费者使用basic.reject或basic.nack声明消费失败 并且消息的requeue参数设为false
- 消息是一个过期的消息无人消费
- 队列满了最早的消息也可能称为死信
如果队列配置了dead-letter-exchange属性指定了一个交换机那么队列中的死信会到这个交换机中,这个交换机就是死信交换机(DLX)
如何给队列绑定死信交换机
- 给队列设置dead-letter-exchange属性指定一个交换机
- 给队列设置dead-letter-routing-key属性设置死信交换机与死信队列的routingkey
TTL
time-to-live 如果一个队列中的消息ttl结束了仍没有被消费则会称为死信ttl超时分为两种情况
- 消息所在的队列设置了存活时间
- 消息本身设置了存活时间
声明死信交换机和队列 基于注解
给队列谁知超时时间小在声明队列时配置x-message-ttl属性
发消息的时候给消息本身设置时间
如何实现发送消息20s后消费者收到消息
- 给消息的目标队列绑定死信交换机
- 消费者监听与死信交换机绑定队列
- 发消息是给消息设置ttl为xxs
延迟队列
ttl结合死信交换机 实现消息发出后消费者延迟收到消息的效果
这种就是延迟队列模式
试用的场景
-
延迟发送短信
-
用户下单如果用户15分钟未支付则取消
-
预约工作会议20分钟后自动通知参加会议的人
延迟队列插件使用的步骤包括 -
声明一个交换机添加delayed属性为true
-
发送消息时添加x-delay头,值为超时时间
惰性队列
消息堆积问题
生产者发送消息的速度超过了消费者处理消息的速度导致消息堆积直到队列存储的信息到达了上线最早接收到的消息可能变为死信就会被丢弃 这就是消息堆积问题
解决消息堆积问题的三种思路
- 增加消费者提高消费能力
- 在消费者内开启线程增快消息的处理速度
- 扩大队列容积 提高堆积上限
惰性队列
惰性队列的特征
- 接收到消息存入磁盘而不是内存
- 消费者消费消息时才会从硬盘读取并加载到内存
- 支持数百万的消息存储
设置一个队列为惰性队列,只需要在声明队列时,指定x-queue-mode属性为lazy即可。可以通过命令行将一个运行中的队列修改为惰性队列:
用springamqp声明惰性队列的两种方式
1 @bean方式
2注解方式
惰性队列的优点
- 存在磁盘上存的消息多
- 没有间接性的page-out性能稳定
惰性队列的缺点
- 存在磁盘上消息的时效性会降低
- 他的性能受限于磁盘的io
集群的分类
RabbitMQ的是基于Erlang语言编写,而Erlang又是一个面向并发的语言,天然支持集群模式。RabbitMQ的集群有两种模式:
- 普通集群:分布式集群把队列扩散到集群的各个节点,提高集群的并发能力
- 镜像集群:主从集群 普通集群的基础上添加了主从备份功能 提交稿了集群的数据可用性
径向集群虽支持主从但主从同步并不是一直强制的,某些情况下可能会有数据丢失的风险 rabbitmq3.8之后用仲裁队列代替镜像集群 底层采用了raft协议确保主从的数据一致性。
普通集群
普通集群又称标准集群
- 会在集群的各个节点间共享部分数据
- 当访问集群某节点时,如果队列不在该节点会从数据所在的节点传递到当前节点并返回
- 队列所在节点宕机 队列中的消息会丢失
镜像集群
镜像集群:本质是主从模式,具备下面的特征:
交换机、队列、队列中的消息会在各个mq的镜像节点之间同步备份。
创建队列的节点被称为该队列的主节点,备份到的其它节点叫做该队列的镜像节点。
一个队列的主节点可能是另一个队列的镜像节点
所有操作都是主节点完成,然后同步给镜像节点
主宕机后,镜像节点会替代成新的主
仲裁队列
仲裁队列:仲裁队列是3.8版本以后才有的新功能,用来替代镜像队列,具备下列特征:
与镜像队列一样,都是主从模式,支持主从数据同步
使用非常简单,没有复杂的配置
主从同步基于Raft协议,强一致