通用防重幂等如何设计?

news2025/1/20 13:18:46

防重与幂等的区别

防重与幂等是在 Web 应用程序和分布式系统中重要而又非常常见的问题。

防重

防重是指在多次提交同样的请求过程中,系统会检测和消除重复的数据,确保相同的数据只会被处理一次,从而避免不必要的重复操作或产生错误的结果。防重通常指人为多次提交请求、系统超时等原因数据重复处理,防止重复处理产生错误。

幂等

幂等是指对同一操作进行多次执行所产生的结果和执行一次的结果是相同的。换句话说,无论进行多少次相同的操作,最终的结果都是一致的。在计算中,幂等操作不会因为重复执行而导致状态的变化。幂等性通常在设计和实现API、网络通信等场景中具有重要意义,因为它保证了对同一资源或操作的多次请求不会产生意外的副作用。

HTTP的幂等性
  • • HTTP GET 方法用于获取资源,不应有副作用,所以是幂等的。比如:GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是 N 次都没有副作用。请注意,这里强调的是一次和 N 次具有相同的副作用,而不是每次 GET 的结果相同。GET http://www.news.com/latest-news这个 HTTP 请求可能会每次得到不同的结果,但它本身并没有产生任何副作用,因而是满足幂等性的。

  • • HTTP HEAD 和 GET 本质是一样的,区别在于 HEAD 不含有呈现数据,而仅仅是 HTTP 头信息,不应有副作用,也是幂等的。有的人可能觉得这个方法没什么用,其实不是这样的。想象一个业务情景:欲判断某个资源是否存在,我们通常使用 GET,但这里用 HEAD 则意义更加明确。也就是说,HEAD 方法可以用来做探活使用。

  • • HTTP OPTIONS 主要用于获取当前 URL 所支持的方法,所以也是幂等的。若请求成功,则它会在 HTTP 头中包含一个名为“Allow”的头,值是所支持的方法,如“GET, POST”。HTTP DELETE 方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和 N 次对系统产生的副作用是相同的,即删掉 ID 为 4231 的帖子。因此,调用者可以多次调用或刷新页面而不必担心引起错误。

  • • HTTP POST 方法用于创建资源,所对应的 URI 并非创建的资源本身,而是去执行创建动作的操作者,有副作用,不满足幂等性。比如:POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子,HTTP 响应中应包含帖子的创建状态以及帖子的 URI。两次相同的 POST 请求会在服务器端创建两份资源,它们具有不同的 URI;所以,POST 方法不具备幂等性。

  • • HTTP PUT 方法用于创建或更新操作,所对应的 URI 是要创建或更新的资源本身,有副作用,它应该满足幂等性。比如:PUT http://www.forum/articles/4231的语义是创建或更新 ID 为 4231 的帖子。对同一 URI 进行多次 PUT 的副作用和一次 PUT 是相同的;因此,PUT 方法具有幂等性。所以,对于 POST 的方式,很可能会出现多次提交的问题,就好比,我们在论坛中发帖时,有时候因为网络有问题,可能会对同一篇贴子出现多次提交的情况。

幂等性的应用场景

幂等性在分布式系统和网络通信中广泛应用。以下是一些常见的应用场景:

1. 交易处理: 在金融系统中,交易处理通常需要保证幂等性,以防止重复执行交易或产生不一致的结果。

2. 支付系统: 支付操作需要保证幂等性,确保多次支付请求不会导致用户重复支付或多次扣款。

3. 订单处理: 在电商平台中,订单处理需要具有幂等性,以避免订单重复创建或多次处理。

4. 消息处理: 消息队列系统中,消费者需要确保处理消息的幂等性,防止消息重复处理或丢失。

5. API设计: 在设计RESTful API时,应该考虑接口的幂等性,以防止客户端多次提交相同的请求。

6. 数据库更新: 数据库操作中,更新操作需要具有幂等性,确保多次更新不会产生重复结果或破坏数据一致性。

幂等性与防重关系

总结起来,防止重复处理是为了避免相同请求或操作的重复执行,而幂等性是一种特性,保证相同的操作在多次执行时会得到相同的结果,并且不会对系统状态产生副作用,从而能够帮助实现防止重复处理的目标。

举例来说,一个更新用户信息的操作,如果是幂等的,那么多次执行相同的更新操作不会产生副作用,用户信息也不会发生额外的改变。因此,防止重复处理的一个常见策略是将更新用户信息的操作设计成幂等的,这样即使多次收到相同的更新请求,也不会对用户信息产生重复的更新。

看起来,防重与幂等似乎在说同一件事情,但其实又有不同的区分。幂等性强调一个操作的重复执行不会改变最终结果,而防重则关注于避免在处理过程中重复处理相同的数据或操作,防重对接口处理结果没有要求。虽然它们是不同的概念,但在分布式场景下,组合使用防重与幂等设计可以更好地保证系统的可靠性和正确性。特别是在面对高并发请求的场景下,合理有效的设计对于系统的正常运行非常重要。

下面我们看下如何设计通用的防重幂等系统。

处理流程

常见web请求包含客户端接口请求、服务调用请求、MQ消费等场景,我们把这些请求方统称为客户端,对应的处理方称为服务端。对于幂等性的处理流程来说,这样来看防重主要分三步:

• 确定唯一请求标识,通过唯一标识,服务端根据标识判断是否重复请求。

• 客户端控制避免重复请求

• 服务端幂等性处理,说白了就是要过滤一下已经收到的请求。要做到这个事,我们需要一个存储来记录收到的请求(唯一标识)。当收到交易请求的时候,我们就会到这个存储中去查询。如果查找到了,那么就不再做查询了,并把上次做的结果返回。如果没有查到,那么我们就记录下来。

大概流程如下:

图片

上面流程有个问题如果让 100% 的请求都到这个存储里去查一下,这会导致处理流程变得很慢。所以,最好是当这个存储出现冲突的时候会报错。也就是说,我们收到交易请求后,直接去存储里记录这个 ID(相对于数据的 Insert 操作),如果出现 ID 冲突了的异常,那么我们就知道这个之前已经有人发过来了,所以就不用再做了。比如,数据库中你可以使用 insert into … values … on DUPLICATE KEY UPDATE … 这样的操作。对于更新的场景来说,如果只是状态更新,可以使用如下的方式。如果出错,要么是非法操作,要么是已被更新,要么是状态不对,总之多次调用是不会有副作用的。

update table set status = “paid” where id = xxx and status = “unpaid”;

网上还有 乐观锁、版本号等其他方式,这些都不标准,我们希望我们有一个标准的方式来做这个事,所以,最好还是用一个 ID。因为我们的幂等性服务也是分布式的,所以,需要这个存储也是共享的。这样每个服务就变成没有状态的了。但是,这个存储就成了一个非常关键的依赖,其扩展性和可用性也成了非常关键的指标。

对此,一般的幂等性的设计如下。

确定唯一请求标识

• 有唯一标识的需要把唯一标识带上

      • 唯一标识通常是业务的唯一ID,这种情况通常用于状态变更操作。比如取消订单场景,订单ID就是唯一标识;

      • 无业务唯一ID,但是有操作记录的,操作记录可以充当唯一标识。这种情况通常是调用下游时先生成本地操作记录,然后调用下游,将操作记录ID充当唯一标识。

• 没有唯一标识的,我们创造唯一标识

      • 在表单中添加一个隐藏的字段或在请求头中添加一个特定的标识token(分布式ID)。服务端在处理请求时,根据请求标识来判断是否为重复请求,如果请求标识已经存在或已被使用,则拒绝处理该请求。

      • 通过其他字段串行化处理,服务端生成唯一标识。比如商品购买,可以通过userId+productId加锁,以用户、商品维度加锁串行化处理,服务端接收请求时先加锁,然后生成订单ID,进行业务处理,业务处理完成后释放锁。

客户端防止重复提交

• 前端页面可以在提交后页面控制按钮禁止点击,防止多次点击;

• 上游服务接口避免重复调用下游;对于超时场景,可以让下游提供查询接口,判断处理状态避免重复调用。

• MQ尽量符合exactly once 语义;

服务端幂等处理

为了实现幂等性,可以采用以下处理流程:

图片

虚线非必须

1. 获取唯一标识: 在每个请求中添加唯一标识,例如通过隐藏字段或请求头。

2. 客户端向服务端请求,带上唯一标识

3. 并发控制(分布式锁): 服务端收到请求后,根据标识判断是否为重复请求。同时,记录已处理的请求标识。

      1. 通过分布式锁: 通过分布式锁(Redis)防止重复请求,如果并发请求可以拒绝重复请求。

      2. 乐观锁和版本号: 在数据库中使用乐观锁机制来实现幂等性。在每次请求之前,先读取数据的版本信息,并在更新数据时检查版本号是否匹配。如果版本号不匹配,说明数据已被其他请求修改,可以拒绝重复请求。

4. 业务数据处理:

      1. 我们先根据唯一标识去存储中去查询。如果查找到了,那么就不再做查询了,并把上次做的结果返回。如果没有查到,那么我们就进行业务处理,并把唯一标识记录下来。

      2. 如果有操作流水,建议基于操作流水做幂等,并将幂等号作为唯一性约束,确保唯一性。如果没有流水,那么基于状态机也是可以的。

      3. 不管怎么样,数据库的唯一性约束都要加好,这是系统的最后一道防线。万一前面的锁失效了,这里也能控制得住不会产生脏数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1447137.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS游戏项目合集【附源码】

文章目录 一:迷宫小游戏二:俄罗斯方块三:压扁小鸟 一:迷宫小游戏 【迷宫游戏】是一款基于HTML5技术开发的游戏,玩法简单。玩家需要在一个迷宫中找到出口并成功逃脱,本项目还有自动寻路(Track&a…

C#系列-Entity Framework 架构(18)

下图展示了EF的整体架构。现在让我们逐个地看看架构的各个组件: EF组件图 EDM(Entity Data Mode 实体数据模型):EDM 由三个主要部分组成:概念模型,映射和存储模型。 Conceptual Model(概念模型&#xff0…

编写Makefile

现在我们将创建一个程序,该程序能够读取次位码文件并打印其中定义的函数名称,以及它们的基本块数,从而显示LLVM库的易用性 什么是Makefile? C语言中,我们使用visual studio开发软件时候,写程序开始时候都…

compile error ESP32cam.h no such file or directory

解决方法 可以参考这篇文章: But first, you will need to download the esp32cam.h library. For this go to Github and download the esp32cam Zip. GitHub - yoursunny/esp32cam: OV2640 camera on ESP32-CAM, Arduino library 具体就是下面的这篇重要的文章 :

aardio 编辑GUI界面,调用 python 脚本示例

aardio 中调用 python 的方法有两种,py3 和 process.python 模块 py3 模块:如果经常要拿到python返回的值或从aardio中传数据给python去处理,aardio和python的交互比较多的话,可以考虑使用py3模块,缺点是:p…

多尺度神经网络新一代创新!精度与速度完美平衡,实现多领域应用落地

多尺度神经网络的设计通常基于对频率原则的理解,目的是为了解决高频成分学习慢的问题。这些网络通过特殊设计,比如给高频成分加更多的权重或者将高频成分平移到低频,来提高学习效率。 为了满足在不同层次上理解和处理数据的需求,…

C++:我真的,服了

即我用C做的一个石头剪刀布的“小”程序 上代码&#xff01;&#xff01;&#xff01; ​ #include <bits/stdc.h>//万能头 #include <windows.h>//定义Sleep函数&#xff0c;Sleep(x)即把程序暂停x毫秒 #include <stdlib.h> /*定义system函数&#xff0c;…

chatglm3-6b使用

源码地址 GitHub - THUDM/ChatGLM3: ChatGLM3 series: Open Bilingual Chat LLMs | 开源双语对话语言模型 创建环境 conda create -n chatglm36 python3.11.7 修改源码中依赖&#xff0c;使得使用cuda&#xff0c;否则太慢了 pip3 install torch2.1.2 torchvision0.16.2 to…

AIGC盛行,带你轻松调用开发

文章目录 前言一、&#x1f4d6;AIGC简介二、&#x1f4e3;开通体验开通模型获取API-KEY 三、&#x1f4dd;基于java实现调用1.设置API-KEY2.体验大语言模型多轮对话演示补充流式输出 3.体验通义千问VL使用官方提供照片本地文件多轮对话流式输出 总结 前言 本篇文章基于java和…

MYSQL笔记:简单的SQL操作和select查询

MYSQL笔记&#xff1a;简单的SQL操作和select查询 文章目录 MYSQL笔记&#xff1a;简单的SQL操作和select查询结构化查询语句SQL库操作表操作CRUD操作单表查询select 查询例子 分页查询与limitlimit 只是对结果条数有限制还是会提高查询效率&#xff1f; order bygroup by多表连…

【数据结构】常见八大排序算法(附动图)

一、前言 关于排序&#xff0c;有一些术语&#xff0c;例如算法的稳定/不稳定&#xff0c;内部排序和外部排序等&#xff0c;需要我们了解一下 稳定&#xff1a;当未排序时a在b前面且ab&#xff0c;排序后a仍然在b前面 不稳定&#xff1a;当未排序时a在b前面且ab&#xff0c;排…

微信小程序的图片色彩分析,解决画布网络图片报错问题,窃取网络图片的主色调

1、安装 Mini App Color Thief 包 包括下载包&#xff0c;简单使用都有&#xff0c;之前写了&#xff0c;这里就不写了 网址&#xff1a;微信小程序的图片色彩分析&#xff0c;窃取主色调&#xff0c;调色板-CSDN博客 2、 问题和解决方案 问题&#xff1a;由于我们的窃取图片的…

【SpringBoot】Validator组件+自定义约束注解实现手机号码校验和密码格式限制

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java对AI的调用开发》 《RabbitMQ》《Spring》《SpringMVC》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 前言一、Cons…

Linux防火墙开放

记录一次问题 写的网络服务无法通信 代码没问题&#xff0c;IP绑定、端口绑定没问题&#xff0c;就是无法进行通信&#xff0c;这里要分2步走。 服务器控制台开放 进入防火墙 添加规则&#xff0c;这里以开放udp的8899端口为例 这里在服务器后台就已经开放了&#xff0c;但此时…

Redis相关介绍

概念 Redis&#xff1a;非关系型数据库&#xff08;non-relational)&#xff0c;Mysql是关系型数据库(RDBMS) Redis是当今非常流行的基于KV结构的作为Cache使用的NoSQL数据库 为什么使用NoSQL 关系型 数据库无法应对每秒上万次 的读写请求 表中的存储记录 数量有限 无法简单…

9.【CPP】List (迭代器的模拟实现||list迭代器失效||list的模拟实现)

介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素。list与forward_…

QGIS打开shp地图

地图shp文件如何打开查看 提示 open sans字体没有安装 字体天下-提供各类字体的免费下载和在线预览服务 这里搜索open sans下载 但是 还是不显示 省份名称。

数据结构——lesson2线性表和顺序表

目录 前言 一、顺序表是什么&#xff1f; 1. 静态顺序表&#xff1a;使用定长数组存储元素 2. 动态顺序表&#xff1a;使用动态开辟的数组存储。 二、接口实现 1.动态顺序表存储 2.基本增删查改接口 (1)初始化顺序表 (2)顺序表摧毁 (3)检查空间 (4)顺序表打印 (5)顺…

【JVM篇】什么是jvm

文章目录 &#x1f354;什么是Java虚拟机&#x1f6f8;Java虚拟机有什么用&#x1f339;Java虚拟机的功能&#x1f388;Java虚拟机的组成 &#x1f354;什么是Java虚拟机 JVM指的是Java虚拟机&#xff0c;本质上是一个运行在计算机上的程序&#xff0c;可以运行 Java字节码文件…

pytorch花式索引提取topk的张量

文章目录 pytorch花式索引提取topk的张量问题设定代码实现索引方法gather方法验证 补充知识expand方法gather方法randint pytorch花式索引提取topk的张量 问题设定 或者说&#xff0c;有一个(bs, dim, L)的大张量&#xff0c;索引的index形状为(bs, X)&#xff0c;想得到一个(…