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默认是推模式

Kafka 和 RocketMQ和RabbitMQ的 区别?

维度

Kafka

RocketMQ

RabbitMQ

核心定位

高吞吐日志流平台

业务级可靠消息

通用消息队列

消费模型

Pull

Push + Pull

Push

吞吐量

非常高

中等

顺序保证

Partition 内

Queue 内

单队列内