一、接口幂等性
接口幂等性:用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生副作用。
支付案例:用户购买商品后支付,支付扣款成功,返回结果的时候网络异常,此时钱已经扣了。如果用户再次点击按钮,进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条,即没有保证接口的幂等性。
二、为什么要保证接口幂等性
在接口调用时一般情况下都能正常返回信息不会重复提交,但在以下情况时可能会出现问题,如:
前端重复提交表单: 在填写一些表格时候,用户填写完成提交,很多时候会因网络波动没有及时对用户做出提交成功响应,致使用户认为没有成功提交,然后一直点提交按钮,这时就会发生重复提交表单请求。
用户恶意进行刷单: 例如在实现用户投票这种功能时,如果用户针对一个用户进行重复提交投票,这样会导致接口接收到用户重复提交的投票信息,这样会使投票结果与事实严重不符。
接口超时重复提交: 很多时候 HTTP 客户端工具都默认开启超时重试的机制,尤其是第三方调用接口时候,为了防止网络波动超时等造成的请求失败,都会添加重试机制,导致一个请求提交多次。
消息进行重复消费: 当使用 MQ 消息中间件时候,如果发生消息中间件出现错误未及时提交消费信息,导致发生重复消费。
使用幂等性最大的优势在于使接口保证任何幂等性操作,免去因重试等造成系统产生的未知的问题。
三、哪些操作需要保证接口幂等性
在增删改查4个操作中,尤为注意就是增加或者修改
1、新增操作
增加在重复提交的场景下会出现幂等性问题,如以上的支付问题
2、删除操作
删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个,在不考虑返回结果的情况下,删除操作也是具有幂等性的)
3、更新操作
修改在大多场景下结果一样,但是如果是增量修改是需要保证幂等性的。如: 把表中id为XXX的记录的A字段值设置为1,这种操作不管执行多少次都是幂等的。把表中id为XXX的记录的A字段值增加1,这种操作就不是幂等的。
4、查询操作
查询对于结果是不会有改变的,查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作。
四、引入幂等性后对系统的影响
幂等性是为了简化客户端逻辑处理,防止重复提交等操作,但增加了服务端的逻辑复杂性和成本,其主要是:
- 把并行执行的功能改为串行执行,降低了执行效率。
- 增加了额外控制幂等的业务逻辑,复杂化了业务功能;
所以在使用时候需要考虑是否引入幂等性的必要性,根据实际业务场景具体分析,除了业务上的特殊要求外,一般情况下不需要引入的接口幂等性。
五、如何保证接口幂等性?
1、前端限制:按钮只操作一次
一般是提交后把按钮置灰或loading状态,消除用户因为重复点击而产生的重复记录,比如添加操作,由于点击两次而产生两条记录。
2、token机制
1)功能上允许重复提交,但要保证重复提交不产生副作用
2)比如点击n次只能产生一条记录,具体实现就是进入页面时申请一个token,然后后面所有的请求都带上这个token,后端根据token来避免重复请求
3)具体步骤:
- 用户访问页面时,浏览器自动发起获取token请求。
- 服务端生成token,保存到redis中,然后返回给浏览器。
- 用户通过浏览器发起请求时,携带该token。
- 在redis中查询该token是否存在,如果不存在,说明是第一次请求,做则后续的数据操作。
- 如果存在,说明是重复请求,则直接返回成功。
- 在redis中token会在过期时间之后,被自动删除。
3、使用Post/Redirect/Get模式
1)在提交后执行页面重定向
2)当用户提交连表单后,跳转到一个重定向的信息页面
3)避免用户按F5刷新导致的重复提交,而且也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退导致同样重复提交的问题。
4、在session存放特殊标志
5、使用唯一索引放置新增脏数据
6、乐观锁
7、分布式锁
8、状态机幂等
9、缓冲队列
10、全局唯一ID
六、测试接口幂等的场景
- 网络波动,可能会引起重复请求
- 用户重复操作,用户在操作时候可能会无意触发多次下单交易
- 使用了失效或超时重试机制
- 页面重复刷新
- 使用浏览器后退按钮重复之前的操作,导致重复提交表单
- 使用浏览器历史记录重复提交表单
- 浏览器重复的HTTP请求
- 定时任务重复执行