当前位置: 首页 > news >正文

高并发下保证接口幂等性

开发中有些接口对幂等性有严格的要求,如增加/扣减积分、用户支付/退款等场景,如果没有做接口的幂等性就会造成一定的资损或者用户投诉等问题。如下是增加积分过程,若接口未做幂等处理,现在由于积分服务响应超时导致Nginx重试:
在这里插入图片描述
会出现由于积分服务没有做接口幂等处理,Nginx重试操作使得积分接口多次被调用,最终会给用户多加了积分。
那么导致接口重复执行的来源有哪些?如何保证接口的幂等性呢?下面我们就这些问题做分析。

1、接口重复执行的场景

  • (1)用户重复提交请求(如用户点击按钮手速快,连续点击了多次提交按钮)或者用户的恶意攻击,导致请求被多次的转发到后端服务上。如下是用户连续点击发送的两次请求到后端:
    在这里插入图片描述
    实际上请求1和请求2我们只需要处理一个请求就好了,但是现在两个请求都过来了,就需要做幂等处理保证业务的一致性。
    (2)分布式环境中服务的超时重试机制,MQ的自带的重试功能或生产者重复生产消息导致接口被重复调用。如下是MQ导致接口多次调用的情况:
    在这里插入图片描述
    以上是常见的几种导致接口被重复的调用的场景,清楚了接口为什么出现被重复调用,下面就来分析几种解决这个问题的方案。

2、整理保证接口幂等性的方案

(1)前端控制方案
前端工程师在用户提交了请求后让提交按钮变成禁用状态或者跳转到其他的页面的方式,可以在一定程度上可以保证不会出现重复提交的问题。
在这里插入图片描述
此方案可以一定程度上防君子但是不防小人,因为有的用户直接拿到接口后刷接口,那么此时就尴尬了。
(2)借助Redis的setnx命令
在这里插入图片描述
用户的请求1和请求2过来之后,我们在积分服务中拿到业务的唯一标识(如订单的id)到Redis中通过setnx命令并设定过期时间(过期时间可以根据业务来设定)来操作,判断setnx命令是否可以操作成功,如果操作成功那么允许执行业务,如果操作失败就不处理业务。

(3)后端发放令牌的方式
在这里插入图片描述
用户请求服务前,首先需要发送一个请求1到后端获取令牌(如JWT),获取到令牌后请求2和请求3(假设请求3是重复请求)都携带令牌请求到后端,后端接受到请求后先验证令牌的有效性,如果令牌是无效的就直接返回;如果令牌是有效的就将令牌保存到数据库中(数据库中设置令牌是唯一键)
(a)保存令牌数据成功就开始处理对应的任务
(b)保存令牌信息失败并且提示是唯一键冲突的异常,那么手动捕获异常并返回“重复请求”的提示给用户。
上面采用的是将令牌存储到数据库的方式实现,也可以使用Redis来实现,如下图所示
在这里插入图片描述
生成令牌后缓存到redis中,然后请求携带令牌到后端,后端首先检查令牌是否有效,如果无效就直接返回;如果令牌有效再去检查令牌是否在Redis中,如果令牌不在Redis中就直接返回,如果在Redis中就删除令牌然后处理业务

(4)数据库的唯一索引机制(去重表机制)
在这里插入图片描述
数据库给请求中的唯一标识的参数做唯一索引(如订单号),这样请求过来之后首先去保存请求参数信息,如果保存成功就可以执行业务;如果保存失败并提示唯一键冲突,直接返回提示用户“请勿重复”提交,在高并发下,此方式的效率比较低。
(5)Redis计数方式
在这里插入图片描述
每次请求到后端之后,首先通过Redis记录一下请求中的唯一标识,然后使用redis计数并且返回计数的结果,如果计数的结果大于1,则表示当前已请求是重复请求;如果计数等于1就可以处理业务。
(6)状态机方式
在这里插入图片描述有些业务可以是用状态机的方案来实现,如请求1的目的是把订单状态从待支付---->待发货,请求2的目的是将把订单状态从待支付---->交易关闭;如果请求1修改成功之后,请求2的操作是无效的底层的sql如下:

update order set status = x where statue = '待支付';

通过返回的影响行数来判断是否操作成功(影响行数为1表示操作成功),如果操作失败可以做对象的提示
结论:
(1)接口出现重复调用的情况有可能来源于前端(用户多次点击或者恶意攻击等),也有可能后端(服务重试机制)
(2)保证接口幂等性的方案有多种,如去重表、Redis计数或者setnx方式、携带令牌方式、状态机方式等都可以实现。
(3)数据库的乐观锁和悲观锁是可以解决并发问题,但是不一定可以保证幂等性问题(如请求1和请求2按照获取锁的先后顺序执行,等于还是执行了两次,可能还是会对业务造成影响)。

相关文章:

  • vscode终端不识别npm 无法解析npm
  • 微信小程序登陆之反向代理
  • llama-factory 微调 Qwen2.5-3B-Instruct
  • python3使用lxml解析xml时踩坑记录
  • 基于STC89C51的太阳自动跟踪系统的设计与实现—单片机控制步进电机实现太阳跟踪控制(仿真+程序+原理图+PCB+文档)
  • 解决stm32引脚如果选择输入模式
  • 响应压缩导致的接口请求response没有响应体问题排查
  • 群晖 DS923 + 功能简介
  • macOS使用brew切换Python版本【超详细图解】
  • 蓝牙音频软件开发--杰理可视化SDK系列学习笔记汇总(持续更新)
  • 求职招聘网站源码,找工作招工系统,支持H5和各种小程序
  • TypeScript 生成指定范围内的随机数,并支持控制小数点后的位数
  • wsl2配置xv6全解(包括22.04Jammy)
  • 针对 pdf.mjs 文件因 MIME 类型错误导致的 Failed to load module script 问题解决方案
  • 【python】OpenCV—Hand Landmarks Detection
  • Siddon算法的公式和参数详细说明
  • 乐维网管平台核心功能解析(三)——告警关联资产
  • [vue]属性绑定
  • OceanBase 4.3.3 AP 解析:应用 RoaringBitmaps 类型处理海量数据的判重和基数统计
  • Word中公式自动标号带章节编号
  • 汕头违建豪宅“英之园”将强拆,当地:将根据公告期内具体情况采取下一步措施
  • 前四个月人民币贷款增加10.06万亿元,4月末M2余额同比增长8%
  • 中哥两国元首共同见证签署《中华人民共和国政府与哥伦比亚共和国政府关于共同推进丝绸之路经济带和21世纪海上丝绸之路建设的合作规划》
  • 重庆发布经济犯罪案件接报警电子地图,企业可查询导航属地经侦服务点
  • 俄副外长:俄美两国将举行双边谈判
  • 在地球另一端的交流,架起2万公里间更多共赢的桥梁