消息队列常见面试题
1、消息队列的选择
(1)kafka:大数据、日志处理、实时流处理(网站活动追踪、监控数据等)
(2)rabbitmq:适合初创公司或传统企业,简单可靠,能满足大部分场景
(3)rocketmq:金融、电商等核心业务,适用于对数据一致性、可靠性要求极高的场景
(4)Apache Pulsar面向云原生,快速发展中
2、RocketMq如何保证消息顺序
消费顺序性需要在生产端、消息端、消费端三个环节协同保障。生产端若采用集群部署,并行发送可能因网络延迟导致消息顺序错乱。解决方案包括:
- 单一生产者:避免集群并行发送。适用于对顺序严格要求的场景(如股票交易)
- 串行发送:若必须集群,通过分布式锁等机制实现串行发送,确保消息按业务顺序投递。
注:顺序性保障会牺牲部分并发性能,需根据业务场景权衡(如秒杀场景更注重并发,可容忍轻微顺序偏差)。
消息端顺序性保证
消息端(Topic与Message Queue)需通过局部有序策略平衡性能与顺序性。全局有序(所有消息进入单个Message Queue)会导致性能瓶颈,实际中通常按业务分组保证局部有序(如同一用户的聊天消息需有序,不同用户间无需严格顺序)。实现方式:
- Message Queue Selector:根据业务标识(如 user ID、订单 ID)路由消息至同一 Message
Queue,确保同一组业务消息在同一队列中有序存储。
消费端顺序性保证
消费端默认并行消费,可能因节点处理速度差异导致顺序错乱,需通过以下机制确保顺序:
- 有序监听器:使用接口,使消费者与Message Queue形成意义对应关系,避免并行消费
- 消费者组协同:同一消费者组内,一个Message Queue仅分配一个消费者,确保消息按队列顺序串行处理。
3、RocketMQ会不会重复消费?如何避免?如何做到幂等
?
重复消费的原因
重复消费通常因消费超时导致消息队列重新投递:当消费端处理消息超时(未及时返回ACK),消息队列会认为消息失败,重新投递消息,导致同一消息被多次处理。
幂等性处理方案
(1)手动ACK机制与本地事务
- 自动ACK风险:自动ACK机制下,无论业务处理成功与否均确认消费,可能导致失败消息无法重试
- 手动ACK确认:将业务操作(如数据库写入)与手动ACK绑定为本地事务
-
业务成功->提交事务并返回(确认消费)
-
业务失败->回滚事务并返回(要求重新投递)
-
但并不能解决因消费超时导致的重复消费问题
(2)基于Redis的幂等性保证
通过Redis存储消息唯一表示(Message ID)及消费状态,确保重复消息仅处理一次
-
存储结构:Key 为 Message ID,Value 为消费状态(消费中、成功、失败);
-
过期时间:需大于消费者处理超时时间,避免超时重试时 Key 已过期;
-
状态流转:
- 首次消费:存储 Message ID,状态设为 “消费中”,执行业务逻辑;
- 超时重试:检测到状态为 “消费中” 时,发送延迟消息等待原消费完成;
- 结果确认:原消费成功则状态更新为 “成功”(重试消息忽略),失败则状态更新为 “失败,(可能触发重试(根据重试策略)