重复请求的幂等接口设计的思考

Posted 梦江黎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重复请求的幂等接口设计的思考相关的知识,希望对你有一定的参考价值。


问题引发场景

问题分析

等定位到此问题的时候,脑海里第一反应就是加锁,单线程就加同步锁,分布式的情况下就通过redis加分布式锁。具体到上述场景,仔细考虑可能存在:

  1. MQ消息出现重复推送的情况

  2. 一条MQ消息里面的内容出现了重复的业务消息 由于是接受MQ推送的消息,而且项目是多实例部署的,可行的方案就是加分布式锁。

想想自己跑到公司到定位到问题,已经凌晨两点了,而发版时间是凌晨6点。由于时间太短,匆匆忙忙加一个分布式锁,实现起来比较复杂,怕仓促之间可能因为其他业务或者其他问题考虑不全面的而引发其他问题。 后来 想到一个既简单又快速的方式,就是通过数据库的操作的方式的顺利实现的(下文会具体介绍)。后来有空的时候,网络搜索此类问题的解决方案,结合自己的理解总结,整理成了笔记。

解决方式

什么是幂等?

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中,即f(f(x)) = f(x).简单的来说就是一个操作多次执行产生的结果与一次执行产生的结果一致。有些系统操作天生就具有幂等性例如数据库的select语句,但更多时候是需要程序员来做保证的,尤其是在分布式系统环境中,接口能不能做到保证幂等性对系统的影响可能是非常大的,例如很常见的支付下单等场景,由于分布式环境中网络的复杂性,用户误操作,网络抖动,消息重复,服务超时导致业务自动重试等等各种情况都可能会使线上数据产生了不一致,造成生产事故。

第一个反应就是加锁,不管是分布式锁还是单机锁,好像加了锁并发问题就真的不存在了似的,确实在很多情况下加锁是能解决问题的,但程序也变成了单线程执行, 还得注意锁不要加错了地方(先要搞清楚程序需要同步的临界区是什么)否则不但没能解决问题还降低系统TPS造成性能影响。

如何解决幂等?

方法一:同步锁

可以使用synchronized和ReadWriteLock去实现,可以保证多个线程并发访问不会有问题,但是无法解决系统集群或者分布式的场景。

方法二:分布式锁

我的思路是用redis去做,只是里面也有很多坑,比如超时时间控制问题,注意解锁问题,具体这里不细谈,感兴趣的朋友可以百度了解,以后有机会单独再写一篇。

方法三:token+唯一索引

根据具体业务场景,单独建立一张控制幂等的token数据表,新建一个token字段,并给此字段加上唯一索引,由数据库自动抛出异常,业务service来捕获最终返回给客户端友好的提示。

方法四:mysql的insert ignore

和方法三类似,只是利用了insert ignore特性,如果表中某个字段建有唯一索引,同样的除了第一次插入返回1外,其余皆返回0。那么先使用insert ignore 插入一条数据到token表中,根据返回的值为0或者1做相应的处理。开篇谈到的线上问题紧急修复方案采取的是此种方法。

 
   
   
 
  1. @Modifying

  2. @Query(nativeQuery = true,value = "insert ignore into token_business (token_bus) values (?1)")

  3. int insertBusiness(String key);

当然啦,根据具体情况还有很多其他解决方式,比如微服务中可以采用全局唯一ID。

解决了上述场景中的幂等问题,同时会产生另一个场景问题,如果A接口的业务逻辑代码出现异常或者其他原因执行了,需要重新调用A接口该如何处理呢?直接删数据库表字段吗?下回详解。

长按二维码,关注我的程序员故事


以上是关于重复请求的幂等接口设计的思考的主要内容,如果未能解决你的问题,请参考以下文章

支付接口的幂等性设计

由表单重复提交引发的幂等性思考

如何避免重复提交?分布式服务的幂等性设计!

阿里面试官:接口的幂等性怎么设计?

接口的幂等设计

接口服务中的幂等性设计和防重保证,详细分析幂等性设计几种实现方法