面试题答案
一键面试1. 解决消息重复消费问题
- 幂等性设计:
- 业务逻辑层面:在消费端处理消息的业务逻辑中,设计成幂等的。例如对于数据库的插入操作,可以先根据唯一键查询,如果数据已存在则不重复插入。以电商订单支付场景为例,订单号是唯一的,在处理支付消息时,先根据订单号查询订单支付状态,若已支付则不重复处理支付逻辑。
- 数据库层面:利用数据库的唯一约束来保证幂等性。比如给相关业务表的关键字段加上唯一索引,当重复消费消息进行插入操作时,数据库会抛出唯一约束冲突异常,消费端捕获异常后可视为幂等处理成功。
- 消费端去重:
- 内存去重:在消费端维护一个内存集合(如Java中的
HashSet
),记录已经处理过的消息ID。当接收到新消息时,先检查消息ID是否在集合中,若在则直接丢弃,若不在则处理消息并将消息ID加入集合。这种方式适合单机消费场景或小规模集群消费场景,因为内存空间有限。 - 分布式去重:对于大规模分布式消费场景,可以使用分布式缓存(如Redis)来实现去重。将处理过的消息ID存入Redis的
Set
数据结构中,利用Redis的原子性操作SADD
,当SADD
返回0时,表示该消息ID已存在,即该消息已被处理过,消费端可丢弃该消息。
- 内存去重:在消费端维护一个内存集合(如Java中的
2. 解决事务状态回查延迟问题
- 合理设置回查策略:
- 缩短回查间隔:在RocketMQ的事务消息配置中,适当缩短事务状态回查的间隔时间。通过调整
transactionCheckMax
(最大回查次数)和transactionCheckInterval
(回查间隔时间)参数,确保在较短时间内能够多次回查事务状态,加快确认事务最终状态的速度。例如,将transactionCheckInterval
设置为较短时间(如5秒),在多次回查后能更快地确认事务状态。 - 异步回查处理:将事务状态回查操作设计为异步处理。在回查线程池中执行回查逻辑,避免回查操作阻塞业务线程,提高系统的整体并发处理能力。这样即使回查延迟,也不会影响正常的业务消息处理。
- 缩短回查间隔:在RocketMQ的事务消息配置中,适当缩短事务状态回查的间隔时间。通过调整
- 事务状态持久化:
- 本地事务表:在发起事务消息的业务系统中,建立本地事务表。在发送事务消息前,先将事务相关信息(如事务ID、业务数据、事务状态等)插入本地事务表,初始状态设为“待确认”。当RocketMQ进行事务状态回查时,直接查询本地事务表获取事务状态,而不是依赖业务系统中可能复杂的业务逻辑来判断事务状态,这样可以加快回查响应速度。
- 状态同步机制:在本地事务执行成功后,及时更新本地事务表的状态为“已提交”,并通过异步机制将该状态同步到RocketMQ(如发送一条状态更新的普通消息到RocketMQ)。这样在RocketMQ回查事务状态时,若先接收到状态更新消息,则可直接确认事务状态,减少回查操作。
- 监控与补偿:
- 实时监控:建立对事务消息状态的实时监控系统,通过RocketMQ提供的监控接口(如Dashboard)或自定义监控指标,实时监控事务消息的回查延迟情况、未确认事务数量等。一旦发现回查延迟过高或未确认事务数量异常,及时发出告警。
- 人工补偿与自动补偿:对于长时间未确认事务状态且回查无结果的情况,提供人工补偿和自动补偿机制。人工补偿即运维人员手动介入,根据业务数据和日志等信息,判断事务最终状态并在RocketMQ中进行相应处理(如标记为已提交或已回滚)。自动补偿则是系统根据一定的规则(如根据事务创建时间、回查次数等),自动尝试确认事务状态或进行回滚操作,确保数据的最终一致性。