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

浅谈网络中接口幂等性设计问题

所谓幂等性设计,就是说,一次和多次请求某一个资源应该具有同样的副作用。用数学的语言来表达就是:f(x) = f(f(x))。

在数学里,幂等有两种主要的定义。 在某二元运算下,幂等元素是指被自己重复运算(或对于函数是为复合)的结果等于它自己的元素。

某一元运算为幂等的时,其作用在任一元素两次后会和其作用一次的结果相同。


文章目录

    • 一、关于幂等性
        • 1、幂等的定义
        • 2、幂等的必要性
        • 3、幂等性
    • 二、对于幂等性的实现
        • 1、源头上--控制重复请求(前端防重)
        • 2、过程上--过滤重复动作
        • 3、结果上--解决重复写风险


一、关于幂等性

1、幂等的定义

所谓幂等性设计,就是说,一次和多次请求某一个资源应该具有同样的副作用。用数学的语言来表达就是:f(x) = f(f(x))。

下面是维基百科中对于幂等的定义:

在数学里,幂等有两种主要的定义。 在某二元运算下,幂等元素是指被自己重复运算(或对于函数是为复合)的结果等于它自己的元素。

某一元运算为幂等的时,其作用在任一元素两次后会和其作用一次的结果相同。

百度百科上是这么说的:

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

简而言之,幂等是指:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。

2、幂等的必要性

根据上面对幂等性的定义我们得知:产生重复数据或数据不一致,这个绝大部分是由于发生了重复请求。

这里的重复请求是指同一个请求在一些情况下被多次发起。

在接口调用时一般情况下都能正常返回信息不会重复提交,不过在遇见以下情况时可能就会出现问题,如:

  • 微服务架构下,不同微服务间会有大量的基于 http,rpc 或者 mq 消息的网络通信。如果超时了,微服务框架会进行重试;
  • 用户交互的时候多次点击,无意地触发多笔交易;
  • MQ消息中间件,消息重复消费;
  • 第三方平台的接口(如:支付成功回调接口),因为异常也会导致多次异步回调;
  • 其他中间件/应用服务根据自身的特性,也有可能进行重试。

举几个支付时的例子:

  • 订单创建接口,第一次调用超时了,然后调用方重试了一次。是否会多创建一笔订单?
  • 订单创建时,我们需要去扣减库存,这时接口发生了超时,调用方重试了一次。是否会多扣一次库存?
  • 当这笔订单开始支付,在支付请求发出之后,在服务端发生了扣钱操作,接口响应超时了,调用方重试了一次。是否会多扣一次钱?

因为系统超时,而调用户方重试一下,会给我们的系统带来不一致的副作用。

3、幂等性

幂等性主要保证多次调用对资源的影响是一致的。其本质是通过唯一标识,标记同一操作的方式,来消除多次执行的副作用。

下一用 SQL DML 命令来理解幂等性操作:

  • 查询(SELECT):查询语句不会对数据产生任何变化,天然具备幂等性;
  • 新增(INSERT):带有唯一索引,重复插入会导致后续执行失败,具有幂等性;不带有唯一索引,多次插入会导致数据重复,不具有幂等性;
  • 修改(UPDATE):直接赋值(score = 1),不管执行多少次 score 都一样,具备幂等性;计算赋值(score = score + 1),每次操作 score 数据都不一样,不具备幂等性;
  • 删除(DELETE):绝对值删除(DELETE FROM … WHERE …),重复多次结果一样,具备幂等性;相对值删除(DELETE top(3) FROM …),重复多次结果不一致,不具备幂等性。

二、对于幂等性的实现

总的来说,我们解决幂等性问题就是要控制对资源的写操作,因此我们可以通过控制重复请求、过滤重复动作、解决重复写风险三种方式分别在源头、过程以及结果上对幂等性问题进行分析解决。

1、源头上–控制重复请求(前端防重)

通过前端防重保证幂等是最简单的实现方式,前端相关属性和JS代码即可完成设置。可靠性并不好,有经验的人员可以通过工具跳过页面仍能重复提交。主要适用于表单重复提交或按钮重复点击。

主要解决方案**:**

  • 控制操作次数,例如:提交按钮仅可操作一次(提交动作后按钮置灰)
  • 及时重定向,例如:下单/支付成功后跳转到成功提示页面,这样消除了浏览器前进或后退造成的重复提交问题。

2、过程上–过滤重复动作

# PRG 模式(前端)

PRG 模式即 POST-REDIRECT-GET。当用户进行表单提交时,会重定向到另外一个提交成功页面,而不是停留在原先的表单页面。这样就避免了用户刷新导致重复提交。同时防止了通过浏览器按钮前进/后退导致表单重复提交。 是一种比较常见的前端防重策略。

# 分布式锁

利用 Redis 记录当前处理的业务标识,当检测到没有此任务在处理中,就进入处理,否则判为重复请求,可做过滤处理。

订单发起支付时,支付系统先去 Redis 中查询是否存在该订单号的 Key。若不存在,则在 Redis 中新增这个Key。

接着查询订单是否已支付,若未支付,则支付完成后删除该订单的Key。

通过Redis做分布式锁,只有当一个请求执行完成后,才能执行下个请求。

# Token 机制实现

通过 Token 机制实现接口的幂等性,这是一种比较通用性的实现方法。

具体流程步骤:

  1. 客户端先发送一个请求去获取 Token,服务端会生成一个全局唯一的 ID 作为 Token 保存在 Redis 中,同时把这个 ID 返回给客户端;
  2. 客户端第二次调用业务请求的时候必须携带这个 Token;
  3. 服务端校验这个 Token,如果校验成功,则执行业务,并删除 Redis 中的 Token;
  4. 如果校验失败,说明 Redis 中已经没有对应的 Token,表示重复操作,直接返回指定的结果给客户端。

# 防重表

对于防止数据重复提交,还有一种解决方案就是通过防重表实现。防重表的实现思路也非常简单。首先创建一张表 作为防重表,同时在该表中建立一个或多个字段的唯一索引作为防重字段,用于保证并发情况下,数据只有一条。 在向业务表中插入数据之前先向防重表插入,如果插入失败则表示是重复数据。

  • 对于防重表的解决方案,可能有人会说为什么不使用悲观锁。悲观锁在使用的过程中也是会发生死锁的。悲观锁是 通过锁表的方式实现的。 假设现在一个用户 A 访问表 A(锁住了表 A),然后试图访问表 B;
  • 另一个用户 B 访问表 B(锁住了表 B),然后试图访问表 A。 这时对于用户 A 来说,由于表 B 已经被用户 B 锁住了,所以用户 A 必须等到用户 B 释放表 B 才能访问。 同时对于用户 B 来说,由于表 A 已经被用户 A 锁住了,所以用户 B 必须等到用户 A 释放表 A 才 能访问。此时死锁就已经产生了。

3、结果上–解决重复写风险

常见的方式有:悲观锁(for update)、乐观锁、唯一约束。

# 悲观锁

假设每一次拿数据,都有认为会被修改,所以给数据库的行或表上锁。

当数据库执行 select for update 时会获取被 select 中的数据行的行锁,因此其他并发执行的 select for update 如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。

# 乐观锁

就是很乐观,每次去拿数据的时候都认为别人不会修改。更新时如果 version 变化了,更新不会成功。

缺点:就是在操作业务前,需要先查询出当前的 version 版本。

另外,还存在一种:状态机控制

例如:支付状态流转流程:待支付 -> 支付中 -> 已支付

具有一定要的前置要求的,严格来讲,也属于乐观锁的一种。

# 唯一约束

这种实现方式是利用 mysql 唯一索引的特性。

  • 因为表中某个字段带有唯一索引,如果插入成功,证明表中没有这次请求的信息,则执行后续的业务逻辑;
  • 如果插入失败,则代表已经执行过当前请求,直接返回。

相关文章:

浅谈网络中接口幂等性设计问题

所谓幂等性设计,就是说,一次和多次请求某一个资源应该具有同样的副作用。用数学的语言来表达就是:f(x) f(f(x))。 在数学里,幂等有两种主要的定义。 在某二元运算下,幂等元素是指被自己重复运算(或对于函数…...

《C Primer Plus》第13章复习题与编程练习

《C Primer Plus》第13章复习题与编程练习复习题1. 下面的程序有什么问题?2. 下面的程序完成什么任务?(假设在命令行环境中运行)3. 假设程序中有下列语句:4. 编写一个程序,不接受任何命令行参数或接受一个命…...

计算机SCI论文应该怎么作图? - 易智编译EaseEditing

计算机SCI论文,作图时要注意以下几个方面的问题: 1.图片的格式要tiff或者eps; 2.文件大小不能超过10M; 3.长和宽也给出了具体要求; 4.色彩模式要RGB或者灰度图; 5.文中的文字字体和大小; …...

【一】kubernetes集群部署

一、docker环境搭建 1、移除以前docker相关包 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine2、配置yam源 sudo yum install -y yum-utilssudo yum-config-manager --ad…...

Docker安装Redis

一、拉取镜像 命令&#xff1a;&#xff1a;docker pull <镜像名称>:<版本号> docker pull redis 二&#xff1a;Docker挂载配置文件 挂载&#xff1a;即将宿主的文件和容器内部目录相关联&#xff0c;相互绑定&#xff0c;在宿主机内修改文件的话也随之修改容…...

在shell中执行一条可执行程序(./a.out) 系统执行的过程

目录 系统调度过程 用户空间角度&#xff1a; 内核角度 1、调用fork创建一个新进程 2、使用_fo_fork创建新进程 3、父进程调用wake_up_new_task尝试唤醒新进程 4、CPU选择一个合适的进程来运行&#xff1b; 5、运行新进程 6、实现负载均衡 系统调度过程 分析在命令行…...

【ArcGIS Pro二次开发】(10):属性表字段(field)的修改

在ArcGIS Pro中&#xff0c;经常会遇到用字段计算器对要素的属性表进行计算。下面以一个例子演示如何在ArcGIS Pro SDK二次开发中实现。 一、要实现的功能 如上图所示的要素图层&#xff0c;要实现如下功能&#xff1a; 当字段【市级行政区】的值为【泉州市】时&#xff0c;将…...

数据结构与算法—散列表

目录 散列表 散列函数 散列冲突解决 1、开放寻址法 1.1 线性探测 1.2 二次探测 1.3 双重散列 2、链表法 使用场景 单词查找 散列表与链表的结合使用LRU 散列表总结 散列表实例 散列表 Word 单词拼写功能&#xff0c;如何实现的&#xff1f;散列表&#xff08;Has…...

计算机网络笔记、面试八股(一)—— TCP/IP网络模型

本章目录1. TCP/IP网络模型1.1 应用层1.1.1 应用层作用1.1.2 应用层有哪些常用协议1.2 运输层1.2.1 TCP与UDP的区别1.2.2 分块传输1.2.3 端口1.3 网络层1.3.1 IP报文1.3.2 IP地址1.3.3 网络号和主机号的获得1.3.4 子网掩码的获得1.3.5 路由1.3.6 IP地址与MAC地址的区别1.3.7 AR…...

Servlet笔记(18):国际化

三个概念 国际化&#xff1a; 意义着一个网站提供不同版本的翻译成访问者的语言或国籍的内容。本地化&#xff1a; 意味着向网站添加资源&#xff0c;以使其适应特定的地理或文化区域。区域设置&#xff1a; 针对某个国家的某个地区的设置。 Servlet可以根据请求者的区域设置…...

kibana搭建(windowslinux)

1.说明 搭建kibana方便查询es库&#xff0c;本文分别对windows和linux版本进行安装&#xff0c;因为es集群版本是7.4.1&#xff0c;所以配套的kibana也是选择相同版本 2.下载 https://artifacts.elastic.co/downloads/kibana/kibana-7.4.1-windows-x86_64.zip https://artifact…...

(pytorch进阶之路)Informer

论文&#xff1a;Informer: Beyond Efficient Transformer for Long Sequence Time-Series Forecasting (AAAI’21 Best Paper) 看了一下以前的论文学习学习&#xff0c;我也是重应用吧&#xff0c;所以代码部分会比较多&#xff0c;理论部分就一笔带过吧 论文作者也很良心的…...

关键词聚类和凸现分析-实战1——亚急性甲状腺炎的

审稿人问题第8页第26行-请指出#是什么意思&#xff0c;并解释为什么亚急性甲状腺炎在这里被列为#8。我认为在搜索亚急性甲状腺炎相关文章时&#xff0c;关键词共现分析应该提供关键词共现的数据。这些结果的实际用途是什么?亚急性甲状腺炎是一种较为罕见但重要的甲状腺疾病&am…...

二叉树——二叉搜索树中的众数

二叉搜索树中的众数 链接 给你一个含重复值的二叉搜索树&#xff08;BST&#xff09;的根节点 root &#xff0c;找出并返回 BST 中的所有 众数&#xff08;即&#xff0c;出现频率最高的元素&#xff09;。 如果树中有不止一个众数&#xff0c;可以按 任意顺序 返回。 假定…...

安装_配置参数解读_集群安装配置_启动选举_搭建启停脚本---大数据之ZooKeeper工作笔记004

这里首先下载zookeeper安装包,可以看到官网地址 找到download 点击下载 找到老一点的,我们找3.5.7 in the archive 点击 然后这里找到3.5.7这一个 然后下载这个-bin.tar.gz这个...

RTMP的工作原理及优缺点

一.什么是RTMP&#xff1f;RTMP&#xff08;Real-Time Messaging Protocol&#xff0c;实时消息传输协议&#xff09;是一种用于低延迟、实时音视频和数据传输的双向互联网通信协议&#xff0c;由Macromedia&#xff08;后被Adobe收购&#xff09;开发。RTMP的工作原理是&#…...

【数据结构与算法】——第八章:排序

文章目录1、基本概念1.1 什么是排序1.2 排序算法的稳定性1.3 排序算法的分类1.4 内排序的方法2、插入排序2.1 直接插入排序2.2 直接插入排序2.3 希尔排序3、交换排序3.1 冒泡排序3.2 快速排序4、选择排序4.1 简单选择排序4.2 树形选择排序4.3 堆排序4.4 二路归并排序5、基数排序…...

在linux中web服务器的搭建与配置

以下涉及到的linux命令大全查阅 https://www.runoob.com/linux/linux-command-manual.htmlvim命令查阅 https://www.runoob.com/linux/linux-vim.htmlscp命令https://www.runoob.com/linux/linux-comm-scp.html首先要有一个请求的服务地址用ssh 进入到linux系统中ssh 请求的服务…...

《Python机器学习》基础代码2

&#x1f442; 逝年 - 夏小虎 - 单曲 - 网易云音乐 目录 &#x1f44a;Matplotlib综合应用&#xff1a;空气质量监测数据的图形化展示 &#x1f33c;1&#xff0c;AQI时序变化特点 &#x1f33c;2&#xff0c;AQI分布特征 相关性分析 &#x1f33c;3&#xff0c;优化图形…...

如何基于MLServer构建Python机器学习服务

文章目录前言一、数据集二、训练 Scikit-learn 模型三、基于MLSever构建Scikit-learn服务四、测试模型五、训练 XGBoost 模型六、服务多个模型七、测试多个模型的准确性总结参考前言 在过去我们训练模型&#xff0c;往往通过编写flask代码或者容器化我们的模型并在docker中运行…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...