MQ的使用场景:
丢消息的几个可能点:

针对以上可能丢数据的点,分别解决如下:
RabbitMQ提供了publisher confirm机制,来保证消息往队列发的过程不丢失。消息发送完成后,会返回一个结果给发送者:
ack publish-confirm
消息发送失败后:
以上,保证了消息发送的可靠性。消息到达队列后,MQ的消息默认放内存,宕机则队列中的消息丢失。 ⇒ 交换机、队列、消息数据持久化



RabbitMQ支持消费者确认机制:消费者处理消息成功后,给MQ发送ack回执,MQ收到ack回执才删除消息。具体,SpringAMQP有三种模式可选择:
manual:业务代码执行结束后,调用api,手动发送ackauto:Spring检测Listener代码是否出现异常,无则自动发送ack,有则抛出异常并返回nack(常用)none:关闭ack,MQ认为,消息你拿走了,就是消费成功了,你前脚拿走,MQ后脚删除消息此外,选择auto时,还可以利用Spring的retry机制,在消费发生异常时,重试一定次数,仍然失败则扔到一个异常交换机里,后续人工处理

消费者确认机制选择了自动确认auto模式,设置了retry重试。此时,消费者消费完消息后,在发ack之前,网络抖动或者消费者服务挂了,ack没发出去。
消费者服务重新running后,就会重复消费这条消息。

解决方式:
进入队列的消息会被延迟消费。场景:

延迟队列 = 死信交换机 + TTL(过期时间)
队列中的消息,符合一条,即为死信:
成为死信的消息,可能被丢弃。此时,如果这个队列配置了dead-letter-exchange属性,指定了一个交换机,则死信被投递到这个交换机(即死信交换机,Dead Letter Exchange,DLX)

声明一个队列simple.queue,其死信交换机为dl.direct

Time-To-Live,存活时间,队列的消息,超过TTL还未被消费,成为死信。TTL超时分为:

如上,同时设置了队列存活时间和消息本身存活时间,自然以最短的为准。队列ttl.queue绑定了死信交换机dl.direct,dl.direct又关联了队列,最终死信会被绑定了死信队列的消费者处理 ⇒ 死信交换机 + TTL = 延迟队列

定时发布时,根据定时的时间戳设置其TTL,写发布逻辑,消费死信队列。时间一到,发布信息进入死信队列,实现定时发布。
在RabbitMQ安装DelayExchange插件:
https://www.rabbitmq.com/community-plugins.html
使用该插件时,正常声明一个交换机,并将其delayed属性设置为true,以下声明 + 消费 延迟队列的消息:

发消息时:setHeader添加x-delay头,指定超时时间

生产快,消费慢,消息堆积,产生死信。解决方式:
特点:
声明队列时,调用方法,设置x-queue-mode为lazy,即为惰性队列:

@RabbitListener里的写法:

生产环境部署RabbitMQ集群,有两种种:
标注集群,特点:

丢东西,所以一般不采用普通集群
创建队列test.queue1的节点node1,为queue1队列的主节点,节点node2备份了queue1队列,为queue1队列的镜像节点。当然,节点node1,也可能是别的队列的镜像节点,相对概念。

问题:主节点完成后,还没同步给镜像节点就宕机,会丢一点点数据 ⇒ 仲裁队列解决
引入仲裁队列,做主从同步时,基于Raft协议,强一致。主节点宕机后,镜像节点利用仲裁队列来保证能正确复制。声明使用仲裁队列的写法:



