消息中间件——如何保证消息幂等性

本文介绍了消息的幂等性概念及其重要性,并详细探讨了在消息队列(MQ)系统中可能出现的非幂等性情况及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是消息的幂等性
幂等:在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。这是百度百科给出的幂等的概念。

消息的幂等性:就是即使多次收到了消息,也不会重复消费。所以保证消息的幂等性就是保证消息不会重复消费,这在开发中是很重要的。比如客户点击付款,如果点击了多次,那也只能扣一次费。

2、MQ的幂等性如何保证
我们所熟悉的RabbitMQ、RocketMQ、kafka,都有可能出现消息的重复发送,这个是MQ无法保障的。但是我们又不能在系统中去重复消费消息,那么就需要我们开发人员去保证消息的幂等性。

2.1 MQ出现非幂等性的情况
2.1.1 生成者重复发送消息给MQ
       生成者把消息发送给MQ之后,MQ收到消息在给生产者返回ack的时候,网络中断了。这时MQ明明已经接收到了消息,但是生产者没接收到确定消息,就会认为MQ没有接收到消息。因此,在网络重新连接后,生产者会把已经发送的消息再次发送到MQ,如果MQ没有去重措施的话,那么就接收到了重复的消息。

2.1.2 MQ重复发送消息给消费者
       消费者从MQ中拉取消息进行消费,当消费者已经消费了消息但还没向MQ返回ack的时候,消费者宕机或者网络断开了。所以消费者成功消费了消息的情况,MQ并不知道。当消费者重启或网络重连后,消费者再次去请求MQ拉取消息的时候,MQ会把已经消费的消息再次发送给消费者,如果消费者没有去重就直接消费,那么就会造成重复消费的情况。便会造成数据的不一致。

2.1.3 Kafka重复消费的场景


2.2 保证消息幂等性的办法
2.2.1 生成者不重复发送消息到MQ
       mq内部可以为每条消息生成一个全局唯一、与业务无关的消息id,当mq接收到消息时,会先根据该id判断消息是否重复发送,mq再决定是否接收该消息。

2.2.2 消费者不重复消费
        消费者怎么保证不重复消费的关键在于消费者端做控制,因为MQ不能保证不重复发送消息,所以应该在消费者端控制:即使MQ重复发送了消息,消费者拿到了消息之后,要判断是否已经消费过,如果已经消费,直接丢弃。所以根据实际业务情况,有下面几种方式:

1、如果从MQ拿到数据是要存到数据库,那么可以根据数据创建唯一约束,这样的话,同样的数据从MQ发送过来之后,当插入数据库的时候,会报违反唯一约束,不会插入成功的。(或者可以先查一次,是否在数据库中已经保存了,如果能查到,那就直接丢弃就好了)。

2、让生产者发送消息时,每条消息加一个全局的唯一id,然后消费时,将该id保存到redis里面。消费时先去redis里面查一下有么有,没有再消费。(其实原理跟第一点差不多)。

3、如果拿到的数据是直接放到redis的set中的话,那就不用考虑了,因为set集合就是自动有去重的。

### 关于消息中间件 MQ 的常见面试问题及答案 #### 如何保证消息没有被重复消费? 为了防止消息被重复消费,通常可以通过以下方法实现: - **幂等性设计**:消费者端需要对每条消息进行幂等性处理,即无论该消息被消费多少次,其最终结果保持一致。例如,在更新数据库记录时,可以使用唯一标识符(如 UUID 或业务 ID)判断是否已经处理过这条消息[^2]。 - **确认机制**:大多数消息队列支持手动确认模式(Acknowledge Mode),只有当消费者成功处理完一条消息后才会向 Broker 发送 ACK 确认信号。如果未收到确认,则 Broker 会在一定条件下重新分发此消息。 ```java // RabbitMQ 手动应答示例代码 Channel channel = connection.createChannel(); channel.basicConsume(queueName, false, (consumerTag, message) -> { try { processMessage(message.getBody()); // 处理消息逻辑 channel.basicAck(message.getEnvelope().getDeliveryTag(), false); // 手动ACK } catch (Exception e) { channel.basicNack(message.getEnvelope().getDeliveryTag(), false, true); // NACK重试 } }, consumerTag -> {}); ``` #### 怎么处理消息丢失的情况? 为了避免消息丢失,可以从以下几个方面入手: - **持久化存储**:开启消息持久化功能,即使发生宕机也能恢复数据[^3]。 - **发布确认**:启用 Publisher Confirms 功能,确保 Producer 将消息发送至 Exchange 后能够得到反馈。 - **事务支持**:某些高级场景下可利用 JMS 提供的 XA Transactions 来保障跨资源操作的一致性。 #### 如何保证消息传递的顺序性? 对于有序性的需求,不同框架有不同的解决方案: - 在 RabbitMQ 中,可通过设置 `hashKey` 实现同一路由键下的消息进入固定队列,并绑定单一 Consumer 进行串行化执行[^4]; - 对于 Kafka 而言,默认情况下每个 Partition 都是由单独线程负责拉取与解析,因而天然具备局部排序特性——只要限定一个 Topic 下只有一个 Partition 即可达成全局序列效果。 #### 使用 MQ 的原因及其优势有哪些? 引入消息中间件的主要目的是优化架构性能以及增强灵活性,具体表现为如下几个维度的优势: - 异步解耦:允许生产方快速完成任务提交而无需等待下游响应,从而提升整体效率; - 流量削峰填谷:缓冲瞬态高峰负载压力,平滑波动曲线; - 日志收集转发:集中管理海量日志流便于后续分析挖掘价值; - 高效通讯桥梁:充当实时通知推送服务的基础组件之一。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值