先科普一下MQ(消息队列)是什么?
消息队列是一种异步的服务间通信方式,适用于无服务器和微服务架构。消息在被处理和删除之前一直存储在队列上。它是一种“先进先出”(FIFO)的数据结构,但有些队列也支持优先级等功能。
这里使用的MQ为RabbitMQ
核心工作模式:
publisher(生产者):发送消息的应用
queue(队列):存储消息的缓冲区
consumer(消费者):接收并处理消息的应用生产者将消息发送到队列后就可以继续执行,而不需要等待消费者的即时响应。
消费者在方便的时候从队列中取出消息并进行处理。
消息队列中间件(如RabbitMQ, Kafka等)负责维护这个队列,并确保消息的可靠传递。
简单来说就像一个快递公司的快递系统,寄件人把快递送到快递员或菜鸟驿站,就可以直接走了,不需要取件人当面来拿,快递服务会将快递送到收件人手上,收件人可以在方便的时候拿走快递
为什么要使用MQ?(使用消息队列的好处)
消息队列的特点主要体现在解耦,异步,削峰
解耦:
在一个紧耦合的系统下,一个服务调用另一个服务,如果该服务发生卡顿或更改,这就直接会影响调用方,从而导致整个系统出于瘫痪状态
解决方案:
通过消息队列,服务间不直接通信。生产者只需把消息发到队列,不关心谁来处理;消费者只从队列取消息,不关心谁发送的。任何一方的故障或变更都不会直接影响另一方,系统架构的容错性和扩展性更好。
异步:
同步调用中,调用方必须阻塞等待被调用方处理完毕并返回结果,这段时间调用方的资源被占用,无法处理其他请求,整体响应时间变长。
解决方案:
发送端发送消息后立即返回,无需等待服务器处理。这极大地减少了请求的响应时间,提高了系统的吞吐量。
削峰填谷:
问题:
系统在高峰期(如秒杀、抢购)会面临瞬时巨大流量,如果直接访问数据库,可能导致数据库连接池被占满、服务器资源耗尽甚至崩溃。
解决方案:
消息队列作为一个高效的缓冲区,可以积压突如其来的请求。消费者按照自身的处理能力,以稳定的速度从队列中消费消息。这样避免了流量洪峰直接冲垮后端系统,保证了系统的稳定性。
MQ如何保证消息可靠性?如何保证消息不丢失?
先解释一下什么是可靠性
消息由生产者将消息发送到MQ,消费者从队列中正常消费消息,这就叫消息可靠性。
再解释一下消息丢失有几种情况?
生产者连接不上MQ
因为网络不可靠因素导致生产者连接不上MQ
解决办法:
开启生产者重试策略
spring:
rabbitmq:
connection-timeout: 1s # 设置MQ的连接超时时间
template:
retry:
enabled: true # 开启超时重试机制
initial-interval: 1000ms # 失败后的初始等待时间
multiplier: 2 # 失败后下次的等待时长倍数,下次等待时长 = 上次的等待时长 * multiplier
max-attempts: 3 # 最大重试次数生产者发送消息但未达到交换机
解决办法:
开启confirm回调机制,每个生产者独立配置
publisher-confirm-type: correlated # 开启publisher confirm机制,并设置confirm类型。是否到达交换机,回调是通过异步实现的消息到达交换机,但没有正确路由到队列
开启return回调机制,RabbitMQ中只有一个,没有到达队列时,失败,进行回调
publisher-returns: true # 开启publisher return机制MQ宕机了,队列中的消息不见了
原因:可能由于某些不可抗拒因素,存储MQ消息的磁盘损坏了
持久化机制
交换机持久化: 默认就是持久化。
队列持久化: 默认就是持久化。
消息机持久化: 默认就是持久化。
消费者收到消息,还没消费,消费者宕机
解决办法:开启自动模式业务正常时,返回ack;业务失败时,返回nack。并设置本地重试策略,当本地重试次数耗尽时,将失败消息路由到错误队列
ack: 成功处理消息,RabbitMQ从队列中删除该消息
nack: 消息处理失败,RabbitMQ需要再次投递消息
总结一下
生产者
- 开启生产者重试策略,保证消息正确到达MQ,当本地重试次数耗尽时,将错误消息写入专门存放错误消息的数据库中,后期通知人工进行处理。
- 开启confirm机制,保证消息正确到达MQ。
- 开启return机制,保证消息正确到达交换机
持久化机制
创建交换机、队列、消息时默认设置的就是持久化,确保MQ宕机后重启不丢失。
消费者:
- 开启自动ack,并设置本地重试策略,当本地重试次数耗尽时,将失败消息路由到错误队列中。
- 监听错误队列,并把消息写入数据库中,后期统计人工进行处理
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。