幂等设计:确保操作的安全与可靠

news/2024/12/2 18:06:35/文章来源:https://www.cnblogs.com/likeguang/p/18582405

目录
  • 一、概述
    • 1.1、什么是幂等
    • 1.2、为什么需要幂等?
  • 二、幂等如何设计
    • 实现幂等的8 种方案
    • select+insert+主键/唯一索引冲突
    • 状态机幂等
    • 抽取防重表
    • token令牌
    • 悲观锁(如select for update)
    • 乐观锁
    • 分布式锁
  • 三、HTTP的幂等

一、概述

在分布式系统和微服务架构中,确保操作的安全性和可靠性至关重要。幂等性(Idempotency)是实现这一目标的重要概念。本文将探讨幂等性的基本原理、应用场景以及设计幂等操作的方法。

在工作中,做幂等的场景蛮多的,恰逢阿里云网盘事件爆发,能够访问到陌生人的图片,那么在幂等上肯定就存在着问题。

所以针对幂等性针对要来做一个总结说明。

1.1、什么是幂等

幂等性是指一个操作无论执行多少次,其结果都是相同的。在数学上,函数 f(x)f(x)f(x) 满足幂等性是指 f(f(x))=f(x)f(f(x)) = f(x)f(f(x))=f(x)。在计算机科学中,幂等操作的一个典型例子是HTTP GET请求,不管你发送多少次GET请求,服务器返回的资源状态都是一致的。

1.2、为什么需要幂等?

在某些提交表单的场景,如果用户一直重复点击,可能会产生两条一样的数据,这种是前端重复提交的场景。还有就是转账场景,出现网络超时,有可能请求到,或者出现网络丢包的情况,这种情况导致重复请求的情况,这种情况如果重试,那么就是需要做好幂等,否则的话就会出现转账多转的情况。

二、幂等如何设计

唯一请求标识符:使用唯一请求ID(如UUID)来标识每个操作。服务器可以使用这个ID来跟踪和记录请求,确保每个请求只执行一次。

一般会生成一个全局性的唯一 id,全局唯一 id 有许多种生成方式,如以下方式

  • UUID:UUID的缺点比较明显,它字符串占用的空间比较大,生成的ID过于随机,可读性差,而且没有递增。

  • 分布式 id:经典分布式 id 就是使用雪花算法来生成唯一 id。

分布式 id 生成算法雪花算法是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs。这种算法由Twitter创建,并用于推文的ID。一个Snowflake ID有64位。

第1位:Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。

接下来前41位是时间戳,表示了自选定的时期以来的毫秒数。接下来的10位代表计算机ID,防止冲突。

其余12位代表每台机器上生成ID的序列号,这允许在同一毫秒内创建多个Snowflake ID。

当然,全局唯一性的ID,还可以使用百度的Uidgenerator,或者美团的Leaf。

实现幂等的8 种方案

如果你调用下游接口超时了,是不是要考虑重试,如果重试的话,下游接口就需要支持幂等。

实现幂等一般有以下方案:

  1. select+insert+主键/唯一索引冲突
  2. 直接insert + 主键/唯一索引冲突
  3. 状态机幂等
  4. 抽取防重表
  5. token令牌
  6. 悲观锁(如select for update,很少用)
  7. 乐观锁
  8. 分布式锁

select+insert+主键/唯一索引冲突

日常开发中,为了实现交易接口幂等,我是这样实现的:

交易请求过来,我会先根据请求的唯一流水号 reqNo字段+userId字段,先select一下数据库的流水表

  • 如果数据已经存在,就拦截是重复请求,直接返回成功;

  • 如果数据不存在,就执行insert插入,如果insert成功,则直接返回成功,如果insert产生主键冲突异常,则捕获异常,开始来做一些补偿处理。

notion image

状态机幂等

很多业务表,都是有状态的,比如转账流水表,就会有0-待处理,1-处理中、2-成功、3-失败状态。转账流水更新的时候,都会涉及流水状态更新,即涉及状态机 (即状态变更图)。我们可以利用状态机实现幂等,一起来看下它是怎么实现的。

比如转账成功后,把处理中的转账流水更新为成功状态,SQL这么写:

很多业务表,都是有状态的,比如转账流水表,就会有0-待处理,1-处理中、2-成功、3-失败状态。转账流水更新的时候,都会涉及流水状态更新,即涉及状态机 (即状态变更图)。我们可以利用状态机实现幂等,一起来看下它是怎么实现的。

比如转账成功后,把处理中的转账流水更新为成功状态,SQL这么写:

update transfr_flow set status=2 where biz_seq=‘666’ and status=1;

如下是伪代码

Rsp idempotentTransfer(Request req){String bizSeq = req.getBizSeq();int rows= "update transfr_flow set status=2 where biz_seq=#{bizSeq} and status=1;"if(rows==1){log.info(“更新成功,可以处理该请求”);//其他业务逻辑处理return rsp;}else if(rows==0){log.info(“更新不成功,不处理该请求”);//不处理,直接返回return rsp;}log.warn("数据异常")return rsp:
}

Java

Copy

抽取防重表

很多时候,我们业务表唯一流水号希望后端系统生成,又或者我们希望防重功能与业务表分隔开来,这时候我们可以单独搞个防重表。当然防重表也是利用主键/索引的唯一性,如果插入防重表冲突即直接返回成功,如果插入成功,即去处理请求。

token令牌

token 令牌方案一般包括两个请求阶段:

  1. 客户端请求申请获取token,服务端生成token返回

  2. 客户端带着token请求,服务端校验token

notion image

  1. 客户端发起请求,申请获取token。
  2. 服务端生成全局唯一的token,保存到redis中(一般会设置一个过期时间),然后返回给客户端。
  3. 客户端带着token,发起请求。
  4. 服务端去redis确认token是否存在,一般用 redis.del(token)的方式,如果存在会删除成功,即处理业务逻辑,如果删除失败不处理业务逻辑,直接返回结果。

悲观锁(如select for update)

什么是悲观锁

通俗点讲就是很悲观,每次去操作数据时,都觉得别人中途会修改,所以每次在拿数据的时候都会上锁。官方点讲就是,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。

悲观锁如何控制幂等的呢?就是加锁呀,一般配合事务来实现。

举个更新订单的业务场景:

假设先查出订单,如果查到的是处理中状态,就处理完业务,再然后更新订单状态为完成。如果查到订单,并且不是处理中的状态,则直接返回

整体的伪代码如下:

begin;  # 1.开始事务
select * from order where order_id='666' # 查询订单,判断状态
if(status !=处理中){//非处理中状态,直接返回;return ;
}
## 处理业务逻辑
update order set status='完成' where order_id='666' # 更新完成
commit; # 5.提交事务
  • 这里面order_id需要是索引主键哈,要锁住这条记录就好,如果不是索引或者主键,会锁表的!

  • 悲观锁在同一事务操作过程中,锁住了一行数据。别的请求过来只能等待,如果当前事务耗时比较长,就很影响接口性能。所以一般不建议用悲观锁做这个事情。

除了会导致事务过长之外,还会导致大量的锁循环检测,导致CPU飙升,一般来说不会利用数据库来热点数据更新。

可以采用的方案有:

  • 如果使用的是阿里云的数据库,可以使用到hit机制;
  • Redis+MQ,利用Redis来抗住压力,然后将压力减小,利用MQ来进行消费,然后来对其进行更新;

乐观锁

悲观锁有性能问题,可以试下乐观锁

什么是乐观锁

乐观锁在操作数据时,则非常乐观,认为别人不会同时在修改数据,因此乐观锁不会上锁。只是在执行更新的时候判断一下,在此期间别人是否修改了数据。

怎样实现乐观锁呢?

就是给表的加多一列version版本号,每次更新记录version都升级一下(version=version+1)。具体流程就是先查出当前的版本号version,然后去更新修改数据时,确认下是不是刚刚查出的版本号,如果是才执行更新

比如,我们更新前,先查下数据,查出的版本号是version =1

select order_id,version from order where order_id='666';

然后使用version =1订单Id一起作为条件,再去更新

update order set version = version +1,status='P' where  order_id='666' and version =1

最后更新成功,才可以处理业务逻辑,如果更新失败,默认为重复请求,直接返回。

流程图如下:

notion image

为什么版本号建议自增的呢?

因为乐观锁存在ABA的问题,如果version版本一直是自增的就不会出现ABA的情况啦。

分布式锁

分布式锁实现幂等性的逻辑就是,请求过来时,先去尝试获得分布式锁,如果获得成功,就执行业务逻辑,反之获取失败的话,就舍弃请求直接返回成功。执行流程如下图所示:

notion image

三、HTTP的幂等

我们的接口,一般都是基于http的,所以我们再来聊聊Http的幂等吧。HTTP 请求方法主要有以下这几种,我们看下各个接口是否都是幂等的。

  • GET方法

  • HEAD方法

  • OPTIONS方法

  • DELETE方法

  • POST 方法

  • PUT方法

GET 方法

HTTP 的GET方法用于获取资源,可以类比于数据库的select查询,不应该有副作用,所以是幂等的。它不会改变资源的状态,不论你调用一次还是调用多次,效果一样的,都没有副作用。

如果你的GET方法是获取最近最新的新闻,不同时间点调用,返回的资源内容虽然不一样,但是最终对资源本质是没有影响的哈,所以还是幂等的。

HEAD 方法

HTTP HEAD和GET有点像,主要区别是HEAD不含有呈现数据,而仅仅是HTTP的头信息,所以它也是幂等的。如果想判断某个资源是否存在,很多人会使用GET,实际上用HEAD则更加恰当。即HEAD方法通常用来做探活使用。

OPTIONS方法

HTTP OPTIONS 主要用于获取当前URL所支持的方法,也是有点像查询,因此也是幂等的。

DELETE方法

HTTP DELETE 方法用于删除资源,它是的幂等的。比如我们要删除id=666的帖子,一次执行和多次执行,影响的效果是一样的呢。

POST 方法

HTTP POST 方法用于创建资源,可以类比于提交信息,显然一次和多次提交是有副作用,执行效果是不一样的,不满足幂等性

比如:POST 的语义是创建一篇帖子,HTTP 响应中应包含帖子的创建状态以及帖子的 URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的 URI;所以,POST方法不具备幂等性。

PUT 方法

HTTP PUT 方法用于创建或更新操作,所对应的URI是要创建或更新的资源本身,有副作用,它应该满足幂等性。

比如:PUT的语义是创建或更新 ID 为666的帖子。对同一 URI 进行多次 PUT 的副作用和一次 PUT 是相同的;因此,PUT 方法具有幂等性。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从仪表盘探索 MongoDB 关键指标

这是 MongoDB 监控系列文章的第七篇,前面几篇文章的链接如下:MongoDB 监控(一) MongoDB 监控(二) MongoDB 监控(三) MongoDB 监控(四) MongoDB 监控(五) MongoDB 监控(六)按照前面系列文章,我们已经采集到 MongoDB 的监控数据了,并且通过 Grafana 和 Nightinga…

微服务引擎 MSE 及云原生 API 网关 2024 年 10 月产品动态

微服务引擎 MSE 及云原生 API 网关 2024 年 10 月产品动态

20222425 2024-2025-1 《网络与系统攻防技术》实验七实验报告

1.实验内容 本周学习内容:本周我们学了web安全的章节,首先我们了解了前端和后端技术,其次我们学习了一些web安全攻防的内容,例如SQL注入和XSS跨站脚本攻击、CSRF以及安全防范的内容。在实验的过程中我们学到了网络欺诈与防范技术。 2.实验过程 主机IP:192.168.35.1 kali(…

基于Bootstrap3的简单柱状图表插件

jchart是一款简单小巧的基于Bootstrap3.x的jquery柱状图表插件。该柱状图片表插件通过简单的设置,就可以生成非常漂亮的水平柱状图,并带有水平和垂直标签以及图表的头部和尾部。 在线演示 下载 使用方法 该jQuery柱状图插件可以通过javascript来调用,也可以直接使用HTML标…

编译OpenCV——jetson嵌入式平台

jetson嵌入式平台的系统为:ubuntu20.04 aarch64 不再研究Ubuntu x64上交叉编译ubuntu aarch64的OpenCV库,因为无法识别到arm的GTK导致编译不进去,最终imshow时会报如下错误:modules/highgui/src/window.cpp:611:error: (-2) The function is not implemented. Rebuild the …

Breakout pg walkthrough Intermediate

nmap ┌──(root㉿kali)-[~/lab] └─# nmap -p- -A 192.168.192.182 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-02 04:50 UTC Nmap scan report for 192.168.192.182 Host is up (0.071s latency). Not shown: 65533 closed tcp ports (reset) PORT STATE SE…

从开发者工具转型 AI 呼叫中心,这家 Voice Agent 公司已服务 100+客户

Retell.ai 的 5 位联创。(图:maginative.com)Retell AI 提供一个平台,用于构建和部署可进行自然、类人对话的 AI voice agent,赋能呼叫中心,替代或辅助人工座席。Retell AI 起初为构建 voice agent 产品的开发者提供 API,现已将重点转向为中型企业提供完整的 AI 呼叫中心…

实验5文档部分代码

实验一找到输入数据中的最大值和最小值 指向x[0]输出最大数 可以 实验二80 s1的内存大小和字符串长度 能 s1存储的内容是字符串"Learning makes me happy",而sizeof(s1)返回的是整个指针类型的大小\ 不能 在原始代码中,通过赋值的方式为s1分配内存空间,并初始化其…

IC Compiler II(ICC II)后端设计流程——超详细

Preface 本文中英文结合(学习一些专有名词),主要介绍ICC II软件进行后端设计的主要流程,在阅读之前需要对数字IC设计流程有一定的了解。 逻辑综合相关知识请查看:Synopsys逻辑综合及DesignCompiler的使用(想了解逻辑综合的可以看看这个,但内容较多) 数字IC设计整体流程…

迁移工具简介

迁移工具能有序、安全、便捷、轻松地将数字资产、服务、IT 资源及应用程序部分或完全迁移到天翼云,同时保证云上业务的可用性、安全性以及连续性。支持 x86、 ARM 同构服务器间迁移,覆盖多种主流操作系统、支持信创适配。本文分享自天翼云开发者社区《迁移工具简介》,作者:…

智慧园区算法视频分析服务器如何确保视频监控系统在极端天气下也能稳定运行?

在面对极端天气条件时,确保智慧园区算法视频分析服务器的稳定运行对于维持关键监控系统的连续性和数据安全性至关重要。以下是一系列措施,旨在保障视频监控系统在诸如暴雨、高温、暴雪等恶劣天气条件下的可靠性和有效性。通过实施这些策略,我们可以最大程度地减少极端天气对…

Docker常用应用之稍后阅读

1.简介 wallabag是一款开源的,可以自托管的稍后阅读工具。提供了浏览器插件和手机客户端,可以很方便的收藏文章用于稍后再看。 wallabag官网,wallabag github地址,wallabag dockerhub 2.部署 2.1.docker部署 cd /docker_data/ mkdir -p wallabag/data cd wallabag vi docke…