Rabbitmq-幂等性

幂等性是确保同一操作多次执行结果一致的概念,常用于解决消息重复消费问题。例如,在数据库中通过主键ID和指纹码实现去重,避免在高并发场景下消息重复处理。当消费者接收到消息时,先检查数据库中是否存在相同指纹码,若不存在则执行业务逻辑,存在则忽略。此方法简单但可能面临数据库写入瓶颈,可通过分库分表解决。

1. 概念

  • 用户对于同一操作发起的一次请求或者多次请求的结果是一致的,比如在mysql数据当中的乐观锁就可以用来保障幂等性。
  • 在业务高峰期最容易产生消息重复消费问题,比如当消费者消费完消息时,在给生产者返回ack时由于网络中断,导致生产者未收到确认信息,该条消息就会重新发送并被消费者消费,但实际上该消费者已成功消费了该条消息,这就造成了重复消费。而幂等性,即消息不会被多次消费,即使我们收到了很多一样的消息。

2. 解决方案:唯一ID+指纹码

  • 在消费者消费前先去数据库查询这条消息的指纹码标识是否存在,没有就执行insert操作,如果有就代表已经被消费了,就无需任何处理
  • 核心思想:数据库主键id去重
  • 唯一ID:业务表的主键id
  • 指纹码:为了区别每次正常操作的码,每次操作时生成指纹码;可以用时间戳+业务编号或者标志位(具体视业务场景而定)

3. 优劣势

  • 优势:实现简单
  • 劣势:高并发场景下有数据库写入瓶颈(解决方案:根据ID进行分库分表算法路由)
### RabbitMQ 中实现业务幂等性的方法 为了确保消息不会因为重复投递而影响业务逻辑,必须设计合理的机制来保证业务的幂等性。通常的做法是在接收到每条消息时记录其唯一标识符,并在后续处理过程中检查该标识符是否已经被处理过。 #### 使用全局唯一的 Message ID 一种常见的做法是利用消息本身的 `message_id` 或者自定义字段作为唯一键[^1]: ```java public void processMessage(DeliverCallback deliverCallback, String consumerTag, Delivery message) { String messageId = new String(message.getProperties().getMessageId()); if (isProcessedBefore(messageId)) { channel.basicAck(message.getEnvelope().getDeliveryTag(), false); return; } try { // 处理业务逻辑 handleBusinessLogic(new String(message.getBody())); // 记录已处理的消息ID markAsProcessed(messageId); // 手动确认消息已被成功处理 channel.basicAck(message.getEnvelope().getDeliveryTag(), false); } catch (Exception e) { // 如果失败,则拒绝这条消息并设置重新排队标志为true channel.basicNack(message.getEnvelope().getDeliveryTag(), false, true); } } ``` 此代码片段展示了如何通过检查消息ID防止重复执行相同的操作。一旦发现之前已经处理过的消息就立即返回而不做任何额外的工作;而对于新到达的消息,在完成相应的业务操作后将其状态更新为“已处理”,最后再向RabbitMQ 发送 ACK 确认信号表示一切正常[^2]。 #### 数据库事务配合乐观锁控制 另一种方式则是借助数据库层面的支持——比如采用版本号或时间戳来进行冲突检测,从而达到幂等的效果。具体来说就是在每次尝试修改数据前先读取当前最新的一版记录,并比较其中包含的时间戳/版本号与预期相符后再提交更改。如果不一致则说明有其他进程抢先一步完成了相同的变更,这时可以选择放弃本次请求或是重试直到成功为止[^5]。 例如可以在订单表中增加一个 version 字段用于追踪每一次的状态变化情况: | order_id | status | created_at | updated_at | version | |----------|------------|--------|---------| | 1001 | processing | 2023-09-18T14:25:00Z| 2023-09-18T14:26:00Z | 1 | 当需要更新某个特定订单的状态时,SQL语句可能看起来像这样: ```sql UPDATE orders SET status='completed',updated_at=NOW(),version=version+1 WHERE order_id=? AND version=? ``` 这里的关键在于最后一个条件部分,它确保了只有当现有记录确实处于我们所期望的那个版本时才会真正触发 UPDATE 操作。否则的话查询将不会匹配到任何行,也就意味着此次更新被忽略了。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值