面试:消息队列问题


参考文档: 深入消息队列MQ,看这篇就够了,万字图文总结!

面试:RabbitMQ相关问题
面试:RocketMQ相关问题
面试:Kafka相关问题

消息队列

消息队列的使用场景

消息队列的主要特点是异步处理,主要目的是减少请求响应时间、解耦、削峰填谷、广播、日志处理等。
解耦合:将几个业务关联的微服务调用修改为基于MQ的异步通知,可以解除微服务之间的业务耦合。同时还提高了业务性能。
流量削峰:将突发的业务请求放入MQ中,作为缓冲区。后端的业务根据自己的处理能力从MQ中获取消息,逐个处理任务。流量曲线变的平滑很多
延迟队列:基于RabbitMQ的死信队列或者DelayExchange插件,可以实现消息发送后,延迟接收的效果。
大数据流式处理:主要是kafka,用于处理用户活动记录、实时数据传输、日志异步存储等场景。

消息的重发补偿解决思路

主要是记录补偿的方式,在做所有事情之前,先把消息记录下来,消息的处理结果可能是:成功、失败、不确定。不确定等价于失败,重发消息直到成功为止,这中间需要保证消息的处理幂等。

如何防止MQ消息被重复消费?消息的幂等性能解决思路

  • 给每一条消息都添加一个唯一id,在本地记录消息表及消息状态,处理消息时基于数据库表的id唯一性做判断
  • 同样是记录消息表,利用消息状态字段实现基于乐观锁的判断,保证幂等
  • 基于业务本身的幂等性。比如根据id的删除、查询业务天生幂等;新增、修改等业务可以考虑基于数据库id唯一性、或者乐观锁机制确保幂等。本质与消息表方案类似。

自己如何实现消息队列

需要支持快速水平扩容,broker+partition,partition放不同的机器上,增加机器时将数据根据topic做迁移,分布式需要考虑一致性、可用性、分区容错性
一致性:生产者的消息确认、消费者的幂等性、Broker的数据同步
可用性:数据如何保证不丢不重、数据如何持久化、持久化时如何读写
分区容错:采用何种选举机制、如何进行多副本同步
海量数据:如何解决消息积压、海量Topic性能下降
性能上,可以借鉴时间轮(延时消息实现方案)、零拷贝、IO多路复用、顺序读写(避免磁盘寻址)、压缩批处理

延时队列的实现方式

参考《总结:延时任务队列的原理与实现

如何保证消息的有序性

其实RabbitMQ是队列存储,天然具备先进先出的特点,只要消息的发送是有序的,那么理论上接收也是有序的。不过当一个队列绑定了多个消费者时,可能出现消息轮询投递给消费者的情况,而消费者的处理顺序就无法保证了。
因此,要保证消息的有序性,需要做的下面几点:

  • 保证消息发送的有序性
  • 保证一组有序的消息都发送到同一个队列
  • 保证一个队列只包含一个消费者

如果是Kafka,那么需要保证

  • 一个topic只能有一个发送者,保证消息发送的有序性。
    一个topic只能有一个分区,避免分区导致消息并发消费,而不是顺序消费。如果非要有多个分区,那么需要有一个key来保证相同key的消息在同一个分区。
    一个分区只能有一个消费者,可以通过锁保证。

常用消息队列对比

ZeroMQ、ActiveMQ->Apollo、RabbitMQ(稳定)、Kafka(快、大数据)、RocketMQ(支持分布式事务)

几款消息队列中间件对比

RabbitMQ:简单易上手,中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ (开源、社区活跃)是不错的选择;
RocketMQ:支持分布式事务、数据准确性保障更可靠,大型公司,基础架构研发实力较强,用 RocketMQ(Java二次开发) 是很好的选择。电商、金融等对事务性要求很高的,可以考虑RocketMQ。
Kafka:高性能、高吞吐量,如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。

死信机制

由于某些原因消息无法被正常消费,为了保证消息不丢失,这些消息就会被投入死信队列。
死信的来源:
(1)消息TTL(存活时间)过期
(2)队列达到最大长度(队列满了,无法再添加数据到mq中)
(3)消息被拒绝(basic.reject或basic.nack)并且requeue=false
使用场景:

  1. 消息堆积报警
    我们可以使用定长队列,如果消费端的消费能力长期小于生产者的生产能力,将会导致大量消息堆积在MQ的队列中,此时使用定长队列,就能在消息溢出时,进入死信交换机->死信队列,只要我们为死信队列建立一个消费着,就可以及时获取到消息堆积情况,并发出报警了
  2. 异常消息检查
    当一些消息因为暂时无法处理,而被消费端拒收时,此时为队列设置死信转发,就能避免让该消息重复入队并被循环获取,同时还让该异常消息备份进死信队列中,而不至于丢失。这些消息后续可以取出进行重新处理,或分析其异常原因
  3. 延迟消费
    可以为队列设置TTL,配合死信机制达到延时队列的效果,对于某些需要延迟处理的消息,可以将其发送到TTL队列中,等待一定的时间后,再将其投递到死信队列中,然后被死信队列消费者消费掉。

RabbitMQ、RocketMQ、Kafka延迟队列实现

  • RabbitMQ本身并不存在延迟队列的概念,在 RabbitMQ 中是通过 DLX 死信交换机和 TTL 消息过期来实现延迟队列的。RabbitMQ的另一个方案是安装DelayExchange插件,可以实现延时队列。
  • RocketMQ 和 RabbitMQ 不同,它本身就有延迟队列的功能,但是开源版本只能支持固定延迟时间的消息,不支持任意时间精度的消息。
    他的默认时间间隔分为 18 个级别,基本上也能满足大部分场景的需要了:1s、 5s、 10s、 30s、 1m、 2m、 3m、 4m、 5m、 6m、 7m、 8m、 9m、 10m、 20m、 30m、 1h、 2h。
    Broker 会根据不同的延迟级别创建出多个不同级别的队列,当我们发送延迟消息的时候,根据不同的延迟级别发送到不同的队列中,同时在 Broker 内部通过一个定时器去轮询这些队列(RocketMQ 会为每个延迟级别分别创建一个定时任务),如果消息达到发送时间,那么就直接把消息发送到指 topic 队列中。
  • 对于 Kafka 来说,原生并不支持延迟队列的功能,需要我们手动去实现,这里我根据 RocketMQ 的设计提供一个实现思路。创建一个单独针对延迟队列的 topic,同时创建 18 个 partition 针对不同的延迟级别,发送消息的时候根据延迟参数发送到延迟 topic 对应的 partition,对应的key为延迟时间,同时把原 topic 保存到 header 中,使用定时器去轮询延迟的TopicPartition,如果到了时间就转发到真实的topic,如果没到则继续等待。

生产上遇到消息堆积怎么处理

可以从三个方面解决:生产端、broker、消费端
生产端:开关降级+消息补偿,通过开关或者降级组件,降低消息生产发送速度,但是针对发送失败的消息,要有相应的补偿机制。
broker端:增加分区数,如果消息分区数小于消费者数量,可通过扩容分区数实现消费加速。
消费者端:扩容消费者数量,如果消费者数量小于topic分区数,扩容消费者数量可提高消费处理速度。
事后:优化消费者程序,提高处理速率,或者扩容消费者。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值