消息队列(面试)
名称 | 简介 |
---|---|
ActiveMQ | Apache 出品的老牌消息中间件,支持多种消息模型,传统企业应用常用,高并发时性能一般。 |
RabbitMQ | 基于 AMQP 协议,性能良好,支持多种消息模型,企业级应用常见,社区活跃。 |
RocketMQ | 阿里开源的分布式中间件,高吞吐、低延迟,支持顺序、事务消息,国内互联网行业广泛使用。 |
Kafka | 高吞吐分布式消息系统,适合大数据实时处理,多副本保证可靠性,水平扩展好。 |
高吞吐量指的是系统在单位时间内能够处理的消息数量非常多。
消息队列使用场景有哪些
解耦:可以在多个系统之间进行解耦,将原本通过网络之间的调用的方式改为使用MQ进行消息的异步通讯,只要该操作不是需要同步的,就可以改为使用MQ进行不同系统之间的联系,这样项目之间不会存在耦合,系统之间不会产生太大的影响,就算一个系统挂了,也只是消息挤压在MQ里面没人进行消费而已,不会对其他的系统产生影响。
异步:
流量削峰:
消息重复消费怎么解决
核心解决思路是 “让消费逻辑具备幂等性”(即重复执行多次与执行一次结果一致),再结合消息中间件的机制辅助控制。
核心:1. 消息生成全局唯一 ID(如 UUID、雪花 ID);
2. 消费前先查 “幂等表”(数据库表 / Redis),判断该 ID 是否已消费;
3. 未消费则执行逻辑,执行后写入幂等表;已消费则直接跳过。
辅助:手动提交 “消费确认”(如 Kafka 的 offset、RabbitMQ 的 ack),避免中间件因未收到确认而重复投递;
消息丢失怎么解决(怎么保证可靠性)
1. 生产端:确保消息成功发送
- 同步确认机制:使用消息中间件,等待 Broker 确认消息已持久化后再返回成功,避免网络波动导致的发送丢失。
- 重试机制:对发送失败的消息(如网络超时),通过有限次数重试(配合指数退避策略)提高成功率,同时记录重试日志便于排查。
2. 中间件:确保消息可靠存储
- 持久化配置:开启 Broker 的持久化(如 Kafka 的分区副本机制、RabbitMQ 的队列持久化 + 消息持久化),避免 Broker 宕机导致内存中消息丢失。
- 集群与副本:部署多副本集群,确保主节点故障时,从副本能接管服务并恢复消息。
3. 消费端:确保消息正确处理
- 手动确认机制:关闭自动确认,在业务逻辑完全处理成功后,再手动发送确认信号。
- 幂等性处理:通过消息唯一 ID(如 UUID)+ 数据库唯一索引 / 分布式锁,避免因重试导致的重复消费,确保消息处理结果一致。
- 失败重试与死信队列:消费失败时,将消息暂存至重试队列(有限次重试),最终失败的消息转入死信队列,人工介入处理,避免消息直接丢弃。
消息队列的顺序性怎么保证
顺序性保证(确保消息按发送顺序消费)
1. 生产端:保证消息按顺序发送
- 对同一业务流(如同一订单的状态变更),通过「分区键」路由到 MQ 的同一个分区 / 队列,确保消息在中间件中物理顺序一致。
2. 中间件:保证消息存储顺序
- MQ 的单个分区 / 队列内部是天然有序的,因此需避免多分区并行写入破坏顺序。
3. 消费端:保证消息按顺序处理
- 单线程消费:一个分区 / 队列仅由一个消费线程处理,避免多线程并行执行导致的顺序错乱。
- 禁止消息跳过:消费失败时,不直接跳过当前消息处理下一条(可通过重试机制等待恢复,或转入死信队列后人工处理)。
消息队列的消息积压怎么解决
消息队列的消息积压通常是由于 “生产速度远大于消费速度” 或 “消费端故障停摆” 导致的
优化一下消费的逻辑,比如之前是一条一条消息消费处理的话,我们可以确认是不是可以优为批量处理消息。如果还是慢,我们可以考虑水平扩容,增加Topic的队列数,和消费组机器的数量,提升整体消费能力。
如何保证数据一致性,事务消息如何实现
为了保证消息的可靠性(比如避免消息丢失),MQ 通常会将消息存储在专门的存储系统中。
我们举个下订单的例子吧。订单系统创建完订单后,再发送ACK消息给下MQ(5)。如果订单创建成功,然后ACK消息没有成功发送出去,MQ就无法感知这个事情,出导致数据不一致。
如何保证数据一致性呢?可以使用事务消息。一起来看下事务消息是如何实现的吧。
事务消息的核心是将本地业务操作(订单创建)和消息发送操作绑定为一个原子性的事务,确保要么两者都成功,要么都失败
事务消息的实现原理(以主流中间件 RocketMQ 为例)
步骤 1:发送 “预处理消息”
- 业务发起方(如订单服务)向 RocketMQ 发送一条 “预处理消息”,内容为后续需通知接收方(如库存服务)的操作(如 “扣减商品 A 库存 10 件”)。
- RocketMQ 收到消息后,不会直接标记为 “可消费”,而是先存储在 “事务消息存储区”,并返回一个 “消息 ID” 给发起方(此时接收方无法消费该消息)。
步骤 2:执行本地事务(业务操作)
- 发起方(订单服务)基于返回的 “消息 ID”,执行本地核心业务(如创建订单,写入订单表)。
- 本地事务执行结果只有两种:成功 或 失败。
步骤 3:确认 / 回滚消息(事务提交 / 回滚)
- 若本地事务成功:发起方向 RocketMQ 发送 “确认消息”(Commit),RocketMQ 将 “预处理消息” 从 “事务存储区” 移到 “普通消息队列”,标记为 “可消费”,接收方(库存服务)即可消费消息并执行扣库存操作。
- 若本地事务失败:发起方向 RocketMQ 发送 “回滚消息”(Rollback),RocketMQ 删除 “预处理消息”,接收方不会收到任何消息,避免无效操作。
步骤 4:RocketMQ 的 “事务回查”(兜底机制)
- 若发起方在步骤 3 中因网络故障、服务宕机等原因,未向 RocketMQ 发送 “确认 / 回滚” 指令,RocketMQ 会主动发起 “事务回查”:
- 根据回查结果,RocketMQ 自动执行 “确认” 或 “回滚”:若订单已创建,则 Commit 消息;若订单未创建,则 Rollback 消息。