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

接口幂等性详解

1. 什么是幂等性

幂等性指的是对同一个操作的多次执行所产生的影响与一次执行的影响相同。无论操作执行多少次,系统状态都应该保持一致。

在计算机科学和网络领域中,幂等性通常用来描述服务或操作的特性。对于RESTful API或HTTP方法,一个幂等操作的特点是:对于相同的输入,在多次执行之后,得到的结果是一致的。

例如,HTTP中的GET请求是幂等的,无论请求多少次,结果都是相同的。而POST请求则不一定是幂等的,因为每次执行时,可能会创建新的资源或状态,从而改变系统状态。

在处理分布式系统和网络通信时,保证操作的幂等性是非常重要的。因为网络中的请求可能因为重试、超时、丢包等原因被多次发送,而服务端必须能够正确处理这些重复的请求,而不会因为重复执行导致系统状态异常或资源多次创建等问题。

确保API的幂等性是设计和开发高质量、健壮系统的一个关键因素,可以减少因为重复操作导致的副作用,保障系统的可靠性和稳定性。

2. 什么是接口幂等性

接口幂等性是指针对同一个操作,无论执行多少次,系统的状态都保持一致。在Web开发中,特别是对于RESTful API,接口的幂等性是一种非常重要的性质。

一个幂等操作满足以下条件:

  1. 多次执行相同操作,产生的效果与执行一次相同操作的效果相同。
  2. 重复执行相同请求的结果与第一次执行该请求的结果相同。

这意味着无论对一个接口发送多少次相同的请求,系统都保持一致的状态,不会因为重复请求而导致状态的变化或资源的重复创建。在幂等性的设计中,服务端需要保证接收到重复请求时,不会因为重复执行操作而引起问题。

幂等性的设计对于解决由于网络延迟、超时、重试、系统问题等导致重复请求的情况非常重要。例如,对于一次性支付的操作,幂等性保证了即使用户在网络不稳定时多次点击支付按钮,系统也只会执行一次支付操作,避免重复扣款。

在RESTful API中,GET请求通常是幂等的,因为它只是获取资源而不会对资源状态做出改变;而POST请求一般不是幂等的,因为每次执行可能会创建新资源或改变系统状态。

确保接口的幂等性对于系统的正确性、可靠性和稳定性至关重要。
在这里插入图片描述

3. 如果忽略接口幂等性设计会出现哪些问题

忽略接口的幂等性设计可能导致以下问题:

  1. 重复操作导致资源状态异常: 如果接口不具有幂等性,重复执行同一个请求可能会导致资源状态的不一致或异常,例如多次创建相同的订单、重复扣款等。

  2. 重复创建资源: 对于非幂等的操作,可能会导致多次执行请求创建重复的资源,例如多次支付,每次请求都会创建新的支付记录。

  3. 系统副作用: 非幂等操作可能会对系统状态造成非预期的副作用,例如多次发送邮件、多次触发同一个事件,导致系统状态不一致。

  4. 用户体验下降: 如果接口不是幂等的,用户在网络波动或系统问题下多次发送相同请求时,可能会产生用户体验问题,例如重复下单、重复支付等。

  5. 系统负载增加: 重复请求可能导致系统负载增加,对服务器和数据库等资源产生不必要的压力,可能导致系统性能下降或宕机。

综上所述,忽略接口的幂等性设计可能导致系统状态不一致、资源重复创建、用户体验问题和系统负载增加等诸多问题,对系统的正确性和稳定性造成威胁。因此,确保接口的幂等性是设计和开发过程中的重要考虑因素。

4. 如何设计接口幂等性

4.1 使用Token+Redis

使用Token和Redis实现接口的幂等性通常涉及以下步骤:

  1. 生成并传递 Token:
    前端生成 Token: 在发送请求之前,前端生成一个唯一的 Token,通常是一个随机生成的字符串,比如 UUID,并将这个Token以客户ID为主键,以Token为值放入Redis
    Token 传递: 将 Token 放置于请求的 Header 或 Body 中。
  2. 服务端处理 Token:
    服务端接收 Token: 接收到请求后,服务端从请求中提取 Token。
  3. 验证 Token:
    检查 Token 是否存在于 Redis 中: 使用 Redis 数据库验证该 Token 是否已存在于数据库中。
  4. 保证幂等性:
    Token 存在: 如果 Token 已存在于 Redis 中,表示该请求已被处理过,服务端直接返回已存储的结果,不执行重复操作。(或者处理业务,并删除Token)
    Token 不存在: 如果 Token 不存在于 Redis 中,表示这是一个新的请求,服务端执行相应的业务逻辑,并将 Token 存储到 Redis,以防止重复请求。(或者直接返回)
    在这里插入图片描述示例伪代码:
    以下是一个简单的伪代码示例,演示了如何使用 Token+Redis 来确保接口的幂等性:
import redis.clients.jedis.Jedis;public class IdempotentAPIHandler {private Jedis jedis;public IdempotentAPIHandler() {this.jedis = new Jedis("localhost", 6379); // 连接 Redis}public String processRequest(String token, String requestData) {if (checkTokenInRedis(token)) {// Token 存在,直接返回已存储的处理结果return jedis.get(token);} else {// Token 不存在,处理请求并将结果存储到 RedisString result = processRequestData(requestData); // 处理请求数据,获取结果jedis.set(token, result, "NX", "EX", 3600); // 设置 Token 并存储结果,设置过期时间为 1 小时return result;}}public boolean checkTokenInRedis(String token) {return jedis.exists(token); // 检查 Token 是否存在于 Redis}public String processRequestData(String requestData) {// 处理请求数据,返回处理结果return "Processed data: " + requestData;}public String generateToken() {// 生成 Token,通常是一个唯一的字符串return "generated_token"; // 这里可以使用 UUID.randomUUID().toString() 等生成唯一标识}public static void main(String[] args) {IdempotentAPIHandler handler = new IdempotentAPIHandler();String token = handler.generateToken(); // 生成 Token// 模拟处理 API 请求String requestData = "some_data";String result = handler.processRequest(token, requestData);System.out.println("Result: " + result);}
}

以上伪代码演示了如何在Java中使用 Token+Redis 实现接口的幂等性。在实际应用中,你需要根据具体的业务需求和框架,对这个示例进行适当调整。

4.2 使用数据库唯一索引

使用数据库唯一索引来实现接口的幂等性是一种可行的方法。这通常涉及在数据库表中创建唯一索引,以确保请求的唯一性。以下是一些步骤和概念:

  1. 设计数据库表结构:
    在数据库中创建一张表来记录请求,可以包括请求的唯一标识、请求内容以及处理结果等字段。

  2. 创建唯一索引:
    使用数据库的唯一索引特性,将请求的唯一标识字段设置为唯一索引。这样,当重复插入具有相同唯一标识的请求时,数据库会拒绝插入重复的记录。

  3. 插入请求前进行唯一索引检查:
    在处理请求前,首先查询数据库检查要插入的唯一标识是否已存在。如果存在,则表明这个请求已经被处理过,可以直接返回已存储的结果。

  4. 处理请求并插入数据库:
    如果唯一索引检查结果表明这个请求是新的,可以执行相应的业务逻辑,并将请求的唯一标识及处理结果插入到数据库中。

示例伪代码:
以下是一个简单的伪代码示例,演示了如何使用数据库的唯一索引来确保接口的幂等性:

// 假设这里是数据库操作对象,这里使用伪代码表示
class DatabaseOperation {// 执行插入操作,以请求的唯一标识作为唯一索引public boolean insertRequest(String uniqueIdentifier, String requestData) {// 在数据库中执行插入操作,使用唯一索引来保证唯一性// 如果遇到唯一索引重复错误(比如DuplicateEntry),说明请求已存在,返回 false// 如果成功插入,说明请求是新的,返回 true}// 根据唯一标识查询请求public String getRequestResult(String uniqueIdentifier) {// 根据唯一标识从数据库中获取请求处理结果}
}// 在处理请求的服务类中
class IdempotentAPIHandler {private DatabaseOperation db;public IdempotentAPIHandler() {this.db = new DatabaseOperation(); // 初始化数据库操作对象}public String processRequest(String uniqueIdentifier, String requestData) {// 检查请求是否已存在if (db.getRequestResult(uniqueIdentifier) != null) {// 请求已存在,直接返回结果return db.getRequestResult(uniqueIdentifier);} else {// 请求是新的,处理请求并将结果插入到数据库String result = processRequestData(requestData); // 处理请求数据,获取结果db.insertRequest(uniqueIdentifier, result); // 插入请求到数据库return result;}}// 其他方法和业务逻辑
}

这个示例演示了如何利用数据库的唯一索引来确保接口的幂等性。在实际应用中,你需要根据具体的数据库和业务需求,调整这个示例。

4.3 使用分布式锁

使用分布式锁来确保接口的幂等性通常会涉及下列步骤:

  1. 获取分布式锁:
    在处理请求之前,尝试获取一个分布式锁,以确保在处理请求期间不会有其他请求来干扰。这样可以确保仅有一个请求能够进入关键的临界区。

  2. 处理请求:
    如果获取了分布式锁,就可以执行请求处理逻辑。这可以是具体的业务逻辑,确保在这个锁的作用下完成了唯一的处理操作。

  3. 释放分布式锁:
    在处理结束后,释放分布式锁,以允许其他请求进入临界区,继续执行接下来的操作。

示例伪代码:
以下是一个使用分布式锁实现接口幂等性的简单示例:

import redis.clients.jedis.Jedis;public class IdempotentAPIHandler {private Jedis jedis;private String lockKey = "request_lock";public IdempotentAPIHandler() {this.jedis = new Jedis("localhost", 6379); // 连接 Redis}public String processRequest(String requestData) {String lockValue = acquireDistributedLock(); // 尝试获取分布式锁if (lockValue != null) {try {// 成功获取锁,处理请求String result = processRequestData(requestData); // 处理请求数据,获取结果return result;} finally {releaseDistributedLock(lockValue); // 处理结束后释放锁}} else {// 获取锁失败,可能有其他请求正在处理中return "Request is being processed. Please try again later.";}}public String acquireDistributedLock() {// 尝试获取分布式锁String lockValue = UUID.randomUUID().toString(); // 生成唯一的锁值String result = jedis.set(lockKey, lockValue, "NX", "EX", 60); // 设置锁,并设置过期时间if ("OK".equals(result)) {return lockValue; // 获取锁成功} else {return null; // 获取锁失败}}public void releaseDistributedLock(String lockValue) {// 释放分布式锁String currentValue = jedis.get(lockKey);if (currentValue != null && currentValue.equals(lockValue)) {jedis.del(lockKey); // 删除锁}}public String processRequestData(String requestData) {// 处理请求数据获取结果return "Processed data: " + requestData;}// 其他方法和业务逻辑
}

这个示例演示了如何使用Redis的分布式锁来确保在处理请求时只有一个请求能够进入关键的临界区,从而保证接口的幂等性。在实际应用中,你需要根据具体的业务需求和框架,对这个示例进行适当调整。

5. 案例:使用Token+Redis保证提交订单接口的幂等性

当设计一个电商网站的提交订单接口时,确保该接口的幂等性对于避免重复订单、重复支付等问题非常重要。以下是一个示例,使用Token+Redis实现提交订单接口的幂等性:

import redis.clients.jedis.Jedis;
import java.util.UUID;public class OrderService {private Jedis jedis;public OrderService() {this.jedis = new Jedis("localhost", 6379); // 连接 Redis}public String submitOrder(String userId, String product) {String token = generateToken(userId, product); // 生成订单的唯一 Tokenif (checkIfOrderExists(token)) {// 订单已存在,直接返回已处理的结果return "Order already submitted.";} else {// 订单不存在,处理订单并存储到 RedisString result = processOrder(userId, product); // 处理订单逻辑,获取结果// 将订单信息存储到 Redis,并设置过期时间为一段合理时间,比如 24 小时jedis.set(token, result, "NX", "EX", 86400);return "Order submitted successfully.";}}private String generateToken(String userId, String product) {// 生成 Token,这里用 userId 和商品信息作为组合生成的 Tokenreturn "order_" + userId + "_" + product + "_" + UUID.randomUUID().toString();}private boolean checkIfOrderExists(String token) {// 检查订单是否已存在于 Redis 中return jedis.exists(token);}private String processOrder(String userId, String product) {// 处理订单的逻辑,可以包括数据库插入、支付等步骤return "Processed Order: " + product + " for User: " + userId;}public static void main(String[] args) {OrderService orderService = new OrderService();// 模拟订单提交String userId = "user123";String product = "ProductABC";String submissionResult = orderService.submitOrder(userId, product);System.out.println(submissionResult);}
}

这个示例演示了一个简单的订单提交接口,确保了订单的幂等性。在实际应用中,你需要根据实际业务需求和框架,对这个示例进行适当调整。

相关文章:

接口幂等性详解

1. 什么是幂等性 幂等性指的是对同一个操作的多次执行所产生的影响与一次执行的影响相同。无论操作执行多少次,系统状态都应该保持一致。 在计算机科学和网络领域中,幂等性通常用来描述服务或操作的特性。对于RESTful API或HTTP方法,一个幂…...

Java操作redis常见类型数据存储

一&#xff0c;Java连接Redis 1.1 导入依赖 打开IDEA在pom.xml导入依赖 注意&#xff1a;要在dependencies标签中导入 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version> &…...

【深度学习】pytorch——Autograd

笔记为自我总结整理的学习笔记&#xff0c;若有错误欢迎指出哟~ 深度学习专栏链接&#xff1a; http://t.csdnimg.cn/dscW7 pytorch——Autograd Autograd简介requires_grad计算图没有梯度追踪的张量ensor.data 、tensor.detach()非叶子节点的梯度计算图特点总结 利用Autograd实…...

【ARM 安全系列介绍 1 -- 奇偶校验与海明码校验详细介绍】

文章目录 奇偶校验介绍奇偶校验 python 实现奇偶校验C代码实现 海明码详细介绍 奇偶校验介绍 奇偶校验是一种错误检测方法&#xff0c;广泛应用于计算机内部以及数据通信领域。其基本原理是为了使得一组数据&#xff08;通常是一字节8位&#xff09;中的“1”的个数为偶数或奇…...

分享34个发布商会PPT,总有一款适合您

分享34个发布商会PPT&#xff0c;总有一款适合您 链接&#xff1a;https://pan.baidu.com/s/1jP9toqTZONWeDIcxvw1wxg?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易。知识付费甚…...

047_第三代软件开发-日志分离

第三代软件开发-日志分离 文章目录 第三代软件开发-日志分离项目介绍日志分离用法 关键字&#xff1a; Qt、 Qml、 log、 日志、 分离 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language&#xff09;和 C 的强…...

ChinaSoft 论坛巡礼 | 系统与网络安全论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…...

Ubuntu Gitlab安javascript:void(‘numberedlist‘)装

原因&#xff1a; 代码越改越多&#xff0c;越难维护&#xff0c;开发代码和发布代码融为一体&#xff1b;2人以上开发&#xff0c;都会修改代码&#xff0c;修改次数一多&#xff0c;代码难以维护 其中&#xff1a;前往Gitlab官网&#xff1a;gitlab/gitlab-ce - Packages pa…...

11.4-GPT4AllTools版本已开始对小部分GPT3.5用户内测推送

OpenAI已经开始小规模推送GPT4 AllTools功能&#xff0c;部分GPT博主已经第一时间体验了此功能&#xff0c;此功能特色是整合目前的多模态功能以及文件上传和联网模块&#xff0c;无需切换&#xff0c;更要全面综合 可上传包括 PDF、数据文件在内的任意文档&#xff0c;并进行分…...

竞赛选题 深度学习手势检测与识别算法 - opencv python

文章目录 0 前言1 实现效果2 技术原理2.1 手部检测2.1.1 基于肤色空间的手势检测方法2.1.2 基于运动的手势检测方法2.1.3 基于边缘的手势检测方法2.1.4 基于模板的手势检测方法2.1.5 基于机器学习的手势检测方法 3 手部识别3.1 SSD网络3.2 数据集3.3 最终改进的网络结构 4 最后…...

语言模型AI——聊聊GPT使用情形与影响

GPT的出现象征着人工智能自然语言处理技术的一次巨大飞跃。从编程助手到写作利器&#xff0c;它的身影在各个行业中越来越常见。百度【文心一言】、CSDN【C知道】等基于GPT的产品相继推出&#xff0c;让我们看到了其广泛的应用前景。然而&#xff0c;随着GPT的普及&#xff0c;…...

浅谈事件冒泡和事件捕获

事件冒泡和事件捕获分别由微软和网景公司提出&#xff0c;这两个概念都是为了解决页面中事件流&#xff08;事件发生顺序&#xff09;的问题。 <div id"div1"><div id"div2"><div id"div3">click</div></div> <…...

CSS 背景、文本、字体

CSS背景&#xff1a; CSS背景属性用于定义HTML元素的背景。CSS属性定义背景效果&#xff1a;background-color&#xff1b;background-image&#xff1b;background-repeat&#xff1b;background-attachment&#xff1b;background-position。 background-color属性定义元素…...

爬取Elastic Stack采集的Nginx内容

以下是一个简单的Go语言爬虫程序&#xff0c;用于爬取Elastic Stack采集的Nginx内容。请注意&#xff0c;这只是一个基本的示例&#xff0c;实际使用时可能需要根据具体情况进行修改和扩展。 package mainimport ("fmt""net/http""io/ioutil" )…...

python 机器学习 常用函数

一 np.random.randint "randint" 是 "random integer" 的缩写&#xff0c;表示生成随机整数。 np.random.randint 是 NumPy 库中的一个函数&#xff0c;用于生成随机整数。以下是该函数的一般语法&#xff1a; np.random.randint(low, high, size)其中…...

手写操作系统篇:环境配置

文章目录 前言C环境配置Rust环境配置Qemu安装 前言 这篇博客先配置好我们之后的开发环境&#xff0c;下载一些依赖的软件包 建议大家使用ubuntu操作系统 C环境配置 sudo apt-get update && sudo apt-get upgrade sudo apt-get install git wget build-essential gdb…...

地理信息系统原理-空间数据结构(7)

​四叉树编码 1.四叉树编码定义 四叉树数据结构是一种对栅格数据的压缩编码方法&#xff0c;其基本思想是将一幅栅格数据层或图像等分为四部分&#xff0c;逐块检查其格网属性值&#xff08;或灰度&#xff09;&#xff1b;如果某个子区的所有格网值都具有相同的值&#xff0…...

三国志14信息查询小程序(历史武将信息一览)制作更新过程03-主要页面的设计

1&#xff0c;小程序的默认显示 分为三部分&#xff0c;头部的标题、中间的内容区和底部的标签栏。点击标签可以切换不同页面&#xff0c;这是在app.json文件中配置的。代码如下&#xff1a; //所有用到的页面都需要在 pages 数组中列出&#xff0c;否则小程序可能会出现错误或…...

学习Opencv(蝴蝶书/C++)相关——2.用clang++或g++命令行编译程序

文章目录 1. c/cpp程序的执行1.1 cpp程序的编译过程1.2 预处理指令1.3 编译过程的细节2. macOS下使用Clang看cpp程序的编译过程2.1 示例2.1.1 第一步 预处理器-preprocessor2.1.2 第二步 编译器-compiler2.1.3 第三步 汇编器-assembler2.1.4 第四步 链接器-linker2.1.5 链接其他…...

【Unity细节】VS不能附加到Unity程序中解决方法大全

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...