MQ死亡九连
MQ死亡九连
为什么用 MQ?
异步处理、削峰填谷、服务解耦
异步处理:缩短请求链路,减少请求时间。比如电商系统,用户购买商品后,积分服务和消息通知服务放到MQ中,消费者异步处理
削峰填谷:防止将后端打爆,请求通过网关之后放入MQ中,后端平缓的处理请求,处理不了的返回请求失败
服务解耦:通过发布/订阅机制降低系统间的强依赖,提高扩展性
MQ的坏处是:
MQ挂了,整个系统可能就挂了
系统复杂度提高,需要处理消息堆积、消息顺序、消息重复
一致性问题
消息队列的模型都有哪些?
队列模型:生产者发送消息到队列中,消费者之间是竞争关系,谁抢到谁消费
发布/订阅模型:生产者发送消息到Topic中,订阅者都能收到消息副本
RabbitMQ 本质是队列模型,通过 Exchange 绑定多个队列来实现发布订阅;
Kafka 则是发布订阅模型,通过消费组机制实现竞争消费。
如何处理消息堆积?
如何保证消息不丢?
消息丢失分为三个阶段
生产阶段:生产者发送到broker的时候丢失(网络波动)
解决办法: 发送确认机制,Broker 确认写入成功,生产者才认为发送成功。发送失败自动重试。最高一致性的解决办法是创建本地消息表,定时任务扫描消息表,补发消息。
存储阶段:broker存储丢失(broker宕机)
解决办法:开启消息持久化
消费阶段:消费者消费时丢失(消费者宕机)
解决办法:手动ACK,处理成功再提交
如何处理重复消息?
基于唯一ID去重:生成全局唯一ID,可以是UUID,也可以是雪花ID,处理消息之前先查阅Redis是否有该ID,有就直接返回/丢弃消息,没有就处理消息并保存ID到Redis
基于业务逻辑本身的幂等性:比如给订单号加唯一索引,如果重复重复插入直接失败。也可以使用状态机,订单状态从"未支付"->"已支付"就不再处理该订单的消息
如何保证消息的顺序?
单一生产者-消费者:单一生产者发送消息到单一队列,单一消费者消费队列中的消息,保证顺序性。但是性能太低了(吞吐量)
分区与顺序键:在支持分区的消息队列中,通过顺序键将消息发送到特定的分区,每个分区是有序的,这样可以保证按照顺序键处理消息
顺序消息队列:一些消息队列(RabbitMQ)支持顺序队列,消息在消费队列中的存储顺序与投递顺序一致。
如何处理堆积消息
先定位消息堆积的原因,如果是消费能力比较弱可以从以下方面入手
增加消费者数量
增加消费者实例,提高并发处理能力
负载均衡,使用消费者组机制,保证消息在消费者之间分布均匀
优化消费逻辑
避免不必要的开销和计算。
调整消息处理速率
流量控制:降低消息生成速率,避免消息堆积进一步恶化
降级处理:对非关键消息进行降级/丢弃,优先处理高等级消息
增加消息队列容量
配置队列参数:根据需求配置消息队列的更大内存或磁盘容量
分区拓展:使用分区拓展,分散消息,提高吞吐量
消息队列设置成推消息还是拉消息
推模式:主动将消息推送给消费者。
优点:实时性强,消息可以立即到达消费者
缺点:难以控制推送速率,可能会造成消费者超载
拉模式:消费者主动从消息队列拉取消息。
优点:可以根据自身能力决定拉取速率,避免过载
缺点:可能会导致消息延迟。
RocketMQ和kafak都通过长轮询实现的拉模式。RabbitMQ默认是推模式