MQ的作用以及应用场景
MQ 就是「中转站 + 缓冲池 + 快递柜」
应用场景:
- 异步解耦:在业务流程中,MQ将一些很耗时但不需要即使返回结果的操作异步化(比如下单以后的短信通知,积分发放,优惠卷返还之类)
- 流量削峰:访问量剧增情况下,比如秒杀或者促销可以使用MQ来控制流量,将请求排队,然后系统逐步处理请求
- 异步通信:很多应用不需要立即处理信息,把一些消息放入MQ中,等到需要处理的时候再慢慢处理
- 消息分发:比如支付成功后,支付系统向MQ发消息,然后其他系统订阅该消息而不需要轮询数据库
- 延迟通知:如果用户下单一定时间内未支付,可以使用延迟队列在超时后自动取消订单
了解哪些MQ,以及区别
- RabbitMQ:功能完备,万级别QPS,界面友好,社区活跃
- Kafka:十万级别QPS、适合大数据、日志、海量消息排队
消息模型
AMQP核心组件:生产者,消费者,交换机,队列,绑定,虚拟主机
说一说RabbitMQ的工作模式
工作模式本质上时生产者,交换机,队列,消费者这几个角色的不同组合方式
- 简单队列:默认Direct交换机,直接发送到队列,单生产者,单消费者
- 工作队列:默认Direct交换机,轮询分发,多个消费者负载均衡
- 发布订阅:Fanout交换机,广播到所有队列
- 路由:Direct交换机,精准路由键匹配,点到点精确路由
- Topic:Topic交换机,通用夫路由键匹配,一对多模式匹配
- RPC:远程调用,通常用于客户端请求服务器的响应
- 发布确认:确保生产者消息发送的可靠性,适合需要确保消息成功到达队列的场景
消息队列如何保证消息不丢失
需要在生产,储存和消费三个阶段共同配合
生产者 :在发送消息时应通过同步或异步的方式接收Broker的响应,做好try-catch异常捕获,若收到写入失败等错误,应进行重试,多次重试失败后触发报警并记录日志
Broker :应该在消息写入磁盘后再返回响应,防止因断电导致内存中消息丢失。在集群环境下启用仲裁队列Raft多副本机制,配置为至少写入两个节点后响应
消费者:关闭自动ACK,必须在业务真正执行完再给Broker发送ACK,防止处理到一半消费者宕机,如果消费者持续处理失败,消息队列会自动重试失败,重试多次还是失败的消息会被扔到私信队列,方便事后排查,避免消息丢失(kafka需要自行实现死信队列)
RabbitMQ的保障机制
它通过Confirm模式保证消息到达Broker,开启后Broker会异步回调通知生产者是否成功。然后必须交换机持久化,队列持久化,消息持久化,三者缺一不可。
最后消费阶段要设成手动ACK
消息队列如何处理重复消息(保证消息的幂等性)
- 基于唯一标识去重:给每条消息分配一个全局唯一ID,可以用UUID,雪花ID或也业务单号。消息方法收到消息后先拿这个ID去Redis或数据库查一下,查到说明处理过直接返回,没查到就执行业务逻辑然后把ID存进去
- 可以利用业务本身幂等性,比如数据库唯一索引和状态机控制
项目中就是雪花ID+先查后写+MySQL唯一索引
Rabbitmq是怎么保证消息序列的顺序性的
Rabbitmq默认情况下一个队列中的小时是按照顺序存储和投递的,若使用单一队列并配合单一消费者进行消费,则可以保证消息的顺序性。对于高并发场景,可以通过多队列策略,格局业务Key(如订单)哈希后路由到不同队列,每个队列由独立的但消费者消费,从而实现部分有序并提升并发能力。
TTL
用于控制消息在队列中的存活时间防止消息无限期积压,避免占用过多系统资源。
消息级别的TTL优先级大于队列级别的TTL。
如果大量消息同时过期怎么办
- 分散过期时间:随机抖动0~60秒
- 增加死信队列消费者的并发度
- 设置死信队列的TTL
- 设置监控与警告,比如一千条积压时警告