消息队列的作用
-
异步通信
-
mysql es redis之间数据同步
-
分布式事务
-
流量削峰
-
定时任务
Rabbitmq
如何保证消息不丢失
![](https://i-blog.csdnimg.cn/blog_migrate/3121425cf7af81ceccde5fe5de8368f2.png)
消息丢失的位置
生产者->交换机(生产者确认机制)
交换机->消息队列(生产者确认机制)
消息队列->消费者(生产者确认机制)
在消息队列中丢失(持久化机制)
生产者处理消息失败(消费者确认机制)
生产者确认机制
![](https://i-blog.csdnimg.cn/blog_migrate/bb810c116e74a9b3a8ed187ed9208fe2.png)
-
如果消息从生产者到消费者过程中都没有丢失,消费者会向生产者发送ack表示收到消息
-
生产者->交换机 和 交换机->消息队列过程如果消息丢失会产生不同的提示信息
-
可以通过回调方法进行消息重发或记录到日志中
持久化机制
MQ默认用内存存储消息,开启持久化功能可以确保缓存在MQ中消息不丢失
持久化分三部分:交换机持久化、队列持久化、消息持久化
消费者确认机制
![](https://i-blog.csdnimg.cn/blog_migrate/d1132999023ca5725359ba453195abcc.png)
消费者处理完消息后可以向MQ发送ack回执,MQ收到ack回执后才会删除该消息
spring提供了三种确认模式
-
手动ack,需要在业务代码结束后,调用手动编写的api发送ack
-
自动ack,由spring监听代码是否出现异常,如果没有异常返回ack,如果有异常返回nack
-
关闭ack,MQ假定消费者获取消息后处理成功,直接删除消息
大多数情况要使用自动ack
当消息处理失败,我们可以利用spring的retry机制,在消费者出现异常时利用本地重试,设置重试次数,如果重试次数达到上限,将消息投递到异常交换机,交给人工处理
如何确保消息不重复消费
![](https://i-blog.csdnimg.cn/blog_migrate/ae7d20dc2f175d535302c1c2f228211e.png)
当消费者处理完消息后如果还没有发送确认消息给队列就发生了网络抖动或者消费者挂了,当网络恢复正常或者消费者重启时队列,队列会再次向消费者发送消息,这就可能导致消息的重复消费
解决方案
每条消息设置一个全局唯一id,消费者校验id判断消息是否消费过
幂等方案:分布式锁、数据库锁
死信交换机
消息满足以下情况就会成为死信
-
消费者拒绝消息,并且消息的request参数位false
-
消息时一个过期消息,超时无人消费
-
要投递的队列消息满了,最早的消息会成为死信
![](https://i-blog.csdnimg.cn/blog_migrate/f5032254cad0477f8189221f61291048.png)
可以设置死信交换机来处理死信
通过死信交换机可以实现延迟队列
可以给消息设置TTL并且结合死信交换机来实现延迟队列
也可以安装delayExchange来实现延迟队列
消息堆积问题怎么解决
当生产者发送消息的速度超过了消费者处理消息的速度,会导致消息堆积,直到队列满了,之后的消息会成为死信,可能会被丢弃
解决消息堆积问题的思路
-
增加消费者,提高消费速度
-
在消费者内开启线程池加快消息处理速度
-
扩大队列容积,提高堆积上限
可以通过惰性队列把消息存储到磁盘上来扩大队列容积
高可用机制
标准集群
-
会在集群的各个节点间共享部分数据:包括交换机、队列元信息,不包括消息
-
当访问集群的某个节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回
-
如果消息队列宕机,消息就会丢失
![](https://i-blog.csdnimg.cn/blog_migrate/c68c42da5af50e9f8cb3ec1d16ae54ce.png)
镜像集群
-
本质上时主从模式
-
交换机、队列、队列中的消息会在各个mq的镜像节点之间同步备份
-
创建队列的节点是主节点,备份到其他节点叫做队列的镜像节点
-
一个队列的主节点可能是另一个队列的镜像节点
-
所有操作都是主节点完成,然后同步给镜像节点
-
主节点宕机,镜像节点会代替主节点
如果主节点宕机镜像节点代替主节点会产生消息丢失可以使用仲裁队列,但是效率会下降