学习
实践
活动
专区
工具
TVP
写文章
专栏首页后端开发你必须学会的干货一文理解如何实现接口的幂等性

一文理解如何实现接口的幂等性

幂等,这个词来源自数学领域。幂等性衍生到软件工程中,它的语义是指:函数/接口可以使用相同的参数重复执行, 不应该影响系统状态,也不会对系统造成改变。

举一个简单的例子:正常设计的查询接口,不管调用多少次,都不会破坏当前的系统或数据,这就是一个幂等操作。

幂等的业务场景

在分布式系统中, 由于分布式天然特性的时序问题以及网络的不可靠性(机器、机架、机房故障、电缆被挖断等等), 重复请求很常见,接口幂等性设计就显得尤为重要。

幂等需要考虑的场景有很多,例如系统A是处理用户客户端发送过来的请求,无论是前端bug、脚本恶意发包、用户重复点击又或是网络超时导致的网络重发,都会造成系统A收到相同参数的网络请求。

对于处理消息队列请求的系统B和处理服务上游发送请求的系统C,也都存在网络超时导致的网络重发,所以要考虑接口的幂等性。

保障幂等性的原理

对于分布式系统来说,在JVM层面的锁已经失去作用,所以保证系统幂等性需要满足3个条件:

  1. 请求唯一标识:每一个请求必须有一个唯一标识。
  2. 处理唯一标识:每次处理完请求之后,必须有一个记录标识这个请求处理过了。
  3. 逻辑判断处理:每次接收请求需要进行判断之前是否处理过的逻辑处理。根据请求唯一标识查询是否存在处理唯一标识。

实际执行中要结合自身业务。

幂等性实现方案

1. token机制

针对客户端重复连续多次点击的情况,例如用户购物提交订单,提交订单的接口就可以通过token机制实现防止重复提交。

主要流程就是:

  1. 服务端提供生成请求token的接口。在存在幂等问题的业务执行前,向服务器获取请求token,服务器会把token保存到Redis中。
  2. 然后调用业务接口请求时,把请求token携带过去,一般放在请求头部。
  3. 服务器判断请求token是否存在redis中:存在则表示第一次请求,这时把Redis中的token删除,继续执行业务;如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行。

这里要结合业务考虑这种场景:如果请求处理失败,前端是否需要重新申请token进行重试(因为此时token在服务端已经被删除)。

2. 数据库唯一索引

往数据库表里插入数据的时候,利用数据库的唯一索引特性,保证唯一的逻辑。唯一序列号可以是一个字段,例如订单的订单号,也可以是多字段的唯一性组合。

事务中包含多表数据的更新,业务要考虑处理事务回滚的问题。

3. Redis实现

Redis实现的方式就是将唯一序列号作为Key存入Redis,在请求处理之前,先查看Key是否存在。唯一序列号可以是一个字段,例如订单的订单号,也可以是多字段的唯一性组合。当然这里需要设置一个key的过期时间,否则Redis中会存在过多的key。具体校验流程如下图所示:

如果想要基于Redis实现幂等性防重框架,需要考虑如下两个问题:

  1. 如果第一次请求失败了,客户端重试,是否需要放行?
  2. 网络请求可能是get或者post(内部rpc协议除外),唯一序列号参数可能在url或是在body体里。则使用防重框架的新接口以及之前老业务接口能否做到版本兼容性?

建议业务使用方最好针对指定业务进行Redis的幂等方案。

Zookeeper同样也能实现上述功能,但由于Zookeeper是CP模型,性能不如Redis,另外针对防重场景,也并不需要Zookeeper高可靠性,所以优先推荐Redis。

4. ON DUPLICATE KEY UPDATE

有些业务场景是先根据索引从表中查询数据是否存在,如果存在则更新状态,不存在才插入数据。

这种情况下在并发量不大的时候没有问题,但是在高并发场景,可能会出现同时插入两条相同索引的情况,导致"Duplicate entry for key 'PRIMARY'"问题。

解决方法首先想到的当然是分布式锁。但分布式锁降低了吞吐量而且分布式锁依赖的组件,如Zookeeper或Redis如果出现网络超时,同样会影响在线服务。

所以一个简单的解决方法是使用mysql的INSERT INTO ...ON DUPLICATE KEY UPDATE语法,从而保证了接口的幂等性。

5. 状态机

对于很多业务,都存在业务流转状态的,每个状态都有前置状态以及最后的结束状态。

以订单为例,已支付的状态的前置状态只能是待支付,而取消状态的前置状态只能是待支付,通过这种状态机的流转就可以控制请求的幂等。假设当前状态是已支付,这时候如果支付接口又接收到了支付请求,则会抛异常或拒绝此次请求。


public enum OrderStatusEnum {
    UN_SUBMIT(0, 0, "待提交"),
    UN_PADING(0, 1, "待支付"),
    PAYED(1, 2, "已支付待发货"),
    DELIVERING(2, 3, "已发货"),
    COMPLETE(3, 4, "已完成"),
    CANCEL(0, 5, "已取消"),
    ;

    //前置状态
    private int preStatus;

    //状态值
    private int status;

    //状态描述
    private String desc;

    OrderStatusEnum(int preStatus, int status, String desc) {
        this.preStatus = preStatus;
        this.status = status;
        this.desc = desc;
    }
    //...
}

6. MVCC方案

这个方案严格上并不是解决幂等问题,更确切来说是解决并发问题。但高并发场景下,也是一种必须的保障措施。

多版本并发控制,该策略主要使用update with condition来保证多次外部请求调用对系统的影响是一致的。在系统设计的过程中,合理的使用乐观锁,通过version或者updateTime(timestamp)等其他条件,来做乐观锁的判断条件,这样保证更新操作即使在并发的情况下,也不会有太大的问题。例如

select * from tablename where condition=@condition //取出要更新的对象,带有版本versoin  
update tableName set name=#name#,version=version+1 where version=@version

在更新的过程中利用version来防止其他操作对对象的并发更新。如果直接拒绝是不理想的操作,则服务端需要一定的事务回滚与重试机制。

7. 分布式锁

有关分布式锁的讲解,可以查看博客《一文理解分布式锁的实现方式

分布式锁同样可以实现接口的幂等性,但由于分布式锁对系统负担来说相对要重一些,可以结合业务场景进行技术选型。

参考文档:

  1. https://zh.wikipedia.org/wiki/冪等
文章分享自微信公众号:
全菜工程师小辉

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!

作者:全菜工程师小辉
原始发表时间:2021-06-15
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • 幂等性学习及接口的幂等性

    1:幂等性的实质是一次或多次请求同一个资源,其结果是相同的。其关注的是对资源产生的影响(副作用)而不是结果,结果可以不同。比如列表查询的时候,一边在save或者...

    凯哥Java
  • 如何理解 RESTful 的幂等性

    HTTP幂等方法,是指无论调用多少次都不会有不同结果的 HTTP 方法。不管你调用一次,还是调用一百次,一千次,结果都是相同的。

    java思维导图
  • 如何理解 RESTful 的幂等性

    HTTP幂等方法,是指无论调用多少次都不会有不同结果的 HTTP 方法。不管你调用一次,还是调用一百次,一千次,结果都是相同的。

    芋道源码
  • 什么是接口幂等性?为什么会产生接口幂等性问题?如何保证接口幂等性?

    博主负责的项目报了一个问题,用户操作回退失效。我们的设计里,操作回退是回到操作前的状态。经过查看日志发现,用户之前的操作做了两次,也就是说提交操作的接口被调用了...

    三分恶
  • 接口的幂等性

    实际开发中在接口设计的时候对于接口的幂等性问题一定要进行考虑的,现对这部分内容做一个梳理

    在水一方
  • Redis如何保证接口的幂等性?

    在最近的一次业务升级中,遇到这样一个问题,我们设计了新的账户体系,需要在用户将应用升级之后将原来账户的数据手动的同步过来,就是需要用户自己去触发同步按钮进行同步...

    一个程序员的成长
  • 如何做到接口的幂等性

    我们实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果。例如:

    用户3467126
  • 接口的幂等性原则

     现如今我们的系统大多拆分为分布式SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用RPC通...

    Lansonli
  • 如何实现下单的幂等性

    十毛
  • 高并发下如何保证接口的幂等性?

    点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 |...

    芋道源码
  • [技术干货]高并发下如何保证接口的幂等性?

    接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题。本文分享了一些解决这类问题非常实用的办法,绝大部分内容我在项目中实践过的,给有需要的小伙伴一个参考...

    全栈程序员站长
  • 高并发下如何保证接口的幂等性?

    接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题。本文分享了一些解决这类问题非常实用的办法,绝大部分内容我在项目中实践过的,给有需要的小伙伴一个参考...

    苏三说技术
  • SpringBoot 实现接口幂等性的 4 种方案!

    点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 |...

    芋道源码
  • 面试必问,如何保证接口的幂等性?

    点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 |...

    芋道源码
  • 高并发下如何保证接口的幂等性?

    接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题。本文分享了一些解决这类问题非常实用的办法,绝大部分内容我在项目中实践过的,给有需要的小伙伴一个参考...

    悟空聊架构
  • 高并发下如何保证接口的幂等性?

    幂等性就是同一个操作执行多次,产生的效果一样。如http的get请求,数据库的select请求就是幂等的

    Java识堂
  • 高并发下如何保证接口的幂等性

    接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题。本文分享了一些解决这类问题非常实用的办法,绝大部分内容我在项目中实践过的,给有需要的小伙伴一个参考...

    码农小胖哥

扫码关注腾讯云开发者

领取腾讯云代金券