重复消息的解决方案

定义:

重复消息又分为重复发送重复消费

重复发送指的是从生产端发送消息保证消息不丢失,但不保证消息不会重发

重复消费指的是消费端重复消费。如消费消息时,一般建议处理业务后,手动提交offset,但是在提交时,程序崩了;再次重启消费应用时,会继续消费上次未提交代码。如:

while(true){
    consumer.poll();//拉取消息
    xxx//业务处理
    consumer.commit();//提交消息位移
}

第三步,提交位移时,程序崩溃再次恢复重复执行第二步的业务

解决方案:

幂等,即相同的请求参数请求1次和请求100次,得到的结果和对业务的影响是一样的。

实现方式:存储+唯一key

唯一key:业务数据的唯一标识,比如订单号

存储:一般是mysql或者redis存储

两种实现方式:强校验和弱校验

  1. 强校验:数据库作为存储,唯一key存储+业务逻辑处理放到一个事务中,要么同时成功,要么同时失败。

    优点缺点
    在并发情况下,只会严格执行一次。数据库唯一性+事务回滚能保证业务只执行一次处理慢:基于磁盘查询,速度慢
    可提供查询流水:处理流水记录的持久化,在某些异常问题排查情况下,能提供查询记录历史数据需要额外处理
    实现回滚:实现难度低,一个注解便可以实现
    适用于资金类业务
  2. 弱校验:使用redis进行唯一key存储和防重。比如订单消息,订单号是一条数据的唯一标识。使用lua脚本设置该消息正在消费中;此时重复消息来,进行相同的设置,发现该订单号,已经被标识为正在处理中,那这条消息放入延时队列中,延时重试吧;如果发现已经消费成功,则直接返回,不执行业务了;业务执行完,设置该key执行成功。 大概过程是这样的

        

        

优点缺点
基于内存,处理速度快数据有过期时间和redis自身特性,造成可能不能防重
无需清理key,redis自动清除比起数据库稍微复杂,需要写lua脚本
消息一定能消费成功架构复杂,为了保证一定能消费成功,引入了延迟队列

注意:

  1. 布隆过滤器也可以防重,占用空间小,速度快,为什么不使用?

    布隆过滤器底层逻辑是,校验一个key如果不存在,绝对不会存在;但是某个key如果存在,那么他是可能存在,又可能不存在。所以这会导致防重查询不准确,最终导致漏消息,这太不能接受了。

  2. 可以使用redis的setnx既能保证并发性,又能保证唯一key,为什么不使用?

    不使用它,主要是redis的 setnx操作和后续的业务执行,不是一个事务单元;即可能setnx成功了,后续业务执行时进程崩溃了,然后在消息重试的时候,又发现setnx里有值了,最终会导致消费失败的消息重试时,造成消息丢失情况。所以才引入了redis lua+延时消息。在lua脚本里记录消费业务的执行状态,延时消息保证消息一定不会丢失。

示例代码:

lua代码:

local status = redis.call('get',KEYS[1]);
if status == nil //不存在,则redis放入唯一key和过期时间
then
   redis.call('SETEX',KEYS[1],ARGV[1],1)
   return "2" //设置成功
else //存在,返回处理状态
  return status 
end
​

消费者伪代码:

//调用lua脚本,获得处理状态
String key = null; //唯一id
int expiredTimeInSeconds = 10*60; //过期时间
 String status = evalScript(key,expiredTimeInSeconds);

if(status.equals("2")){//设置成功,继续业务处理
  //业务处理
}

if(status.equals("1")){ //已在处理中
 //发送到延时队列
}

if(status.equals("3")){ //已处理成功
 //什么都不做
}

总结

  1. 生产端的重复发送和消费端的重复消费会导致消息重复

  2. 解决重复消息的底层逻辑是幂等

  3. 实现幂等的核心思想是:唯一key+存储

  4. 具体实现方式:基于数据库强校验和基于redis的弱校验

参考:技术大佬问我 订单消息重复消费了 怎么办? - 掘金

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值