分布式接口幂等性设计实现

2023-10-28

面对分布式架构和微服务复杂的系统架构和网络超时服务器异常等带来的系统稳定性问题,分布式接口的幂等性设计显得尤为重要。本文简要介绍了几种分布式接口幂等性设计实现,包括Token去重机制、乐观锁机制、数据库主键和状态机实现等,以加深理解。


1、分布式接口幂等性相关概念
1.1 什么是幂等性

幂等性来源自数学领域,数学上的幂等性是指对于某一元运算为幂等的操作,在任意元素上多次执行的结果是相同的。例如,函数f(x) = f(x)对于任意的x,在x上的第一次和第二次执行可以得到相同的结果。

在HTTP/1.1规范中幂等性的定义如下:

Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

在HTTP协议中,HTTP GET是一个清晰的幂等操作,HTTP DELETE/POST是非幂等的,HTTP PUT也是幂等的,因为对同一个URI进行多次PUT的side-effetcs是一致的。

在这里插入图片描述

在分布式架构或者微服务架构中,由于分布式自身的时序问题以及系统网络的稳定性,接口具有成功、失败和无响应的三种状态,为了提供系统的可用性,重复提交是不可避免的,而重试就会引发幂等性的问题。

1.2 幂等性的使用场景

分布式接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的。幂等性适用于以下场景:

  1. 前端重复提交:在订单系统中用户在前端提交订单,快速重复点击多次,造成后端生成多个内容重复的订单,但是后台应该只产生一个订单。
  2. 接口超时重试:对于给第三方调用的接口,为了防止网络抖动或其他原因造成请求丢失,这样的接口一般都会设计成超时重试多次。防止外部多次调用对系统数据状态的发生多次改变,将服务接口设计成幂等,就是为了防止多次重试造成系统不一致的问题。比如账户扣款操作超时重试了多次,理应只扣款一次。
  3. 消息重复消费:MQ消息中间件,消息重复消费,相同请求条件下这次消费的结果与下一次应该保持一致。
1.3 分布式接口幂等性的实现方案

接口幂等性的解决方案可以在客户端和服务端实现,但是客户端控制效果不佳,比如按钮置灰、不可点击等,由于涉及到多设备兼容性以及接口调用的问题,并不能真正实现幂等。因此安全的措施还是从后端接口层进行控制,有以下几种方案:

  1. Token去重:根据业务的操作和内容生成一个Token值(全局唯一ID),在执行操作前先根据这个全局唯一ID进行校验,来判断这个操作是否已经执行。如果存在则表示该方法已经执行。
  2. 乐观锁机制:适用于更新操作。在查询和删除操作中使用乐观锁机制,保证一次处理结果,避免重复操作。设计表结构时使用乐观锁,通过version来做乐观锁,这样既能保证执行效率,又能保证幂等。
  3. 数据库主键:适用于插入时的幂等性。利用数据库中主键唯一约束的特性,保证一张表中只能存在一条带该唯一主键的记录。
  4. 状态机幂等:根据业务表的状态特性设计,只支持状态的单向改变,在执行的时候加上状态信息,实现幂等。

幂等性设计简化了客户端的处理逻辑,却增加了服务端逻辑处理和设计上的复杂性,增加额外控制幂等的业务逻辑的同时,将并行执行改为串行降低了执行效率。

2、几种接口幂等性方案介绍
2.1 Token去重

Token机制是通过在服务端生成一个唯一的Token,并将其存储在客户端中,来保证多个客户端之间对同一个服务的请求结果的一致性。Token机制的实现原理如下:

  1. 服务端生成Token:服务端需要生成一个唯一的Token,可以使用时间戳、随机数等信息来生成。生成Token后,将其存储在服务端的数据库中。
  2. 客户端获取Token:客户端在每次请求服务时,需要向服务端发送一个请求Token。请求Token是服务端根据Token生成的唯一标识,客户端通过该Token来识别自己的身份,并在服务端的数据库中查找对应的Token。
  3. 如果找到了对应的Token,则说明该请求是第一次请求,服务端将其存储在数据库中,并返回一个唯一的标识符;如果在数据库中找不到该Token,则说明该请求是重复请求,服务端不返回任何结果,并提示用户重新操作。
  4. 如果在数据库中也找不到该Token,则说明该请求是幂等请求,服务端可以直接返回结果,不做任何操作。

在这里插入图片描述

Token机制的优点是实现简单、易于部署和维护,能够保证分布式系统的幂等性。但是,它也存在一些局限性,例如需要在服务端和客户端之间传递Token,可能会导致性能问题;另外,如果Token被滥用,也可能会带来安全问题。因此,在使用Token机制时,需要根据具体情况进行权衡和选择。

2.2 乐观锁机制

数据库乐观锁方案一般适用于更新操作的幂等性,实现逻辑是在对应的数据表中添加一个version字段,作为当前数据的的版本标识。这样每次对这条数据执行更新时,都会将该版本标识作为一个条件,值需要为上次待更新数据中的版本标识的值。

1)先根据条件查询数据,得到对应的版本号version

select version from tablename where xxx

2)更新数据时带上版本号version,只有版本号匹配才会更新数据,如果不匹配则不更新

update tablename set count=count+1, version=version+1 where version=#{version}

3)更新数据的时候,同时需要更新数据对应的版本号version,这样可以解决ABA问题。

如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。

乐观锁机制实际上是牺牲了并发性来实现更新操作的幂等性,在并发场景下会导致大量的锁冲突等待和性能问题。

2.3 数据库主键

数据库唯一主键机制是利用主键的唯一性约束,适用于插入操作的幂等性,当插入主键重复的数据时会抛出异常,保证数据的一致性。表结构设计如下所示:

CREATE TABLE `t_check` (
  `id` int(11) NOT NULL COMMENT 'ID',
  `serial_no` varchar(255)  NOT NULL COMMENT '唯一序列号',
  `source_type` varchar(255)  NOT NULL COMMENT '资源类型',
  `status` int(4) DEFAULT NULL COMMENT '状态',
  PRIMARY KEY (`id`)
  UNIQUE KEY `key_s` (`serial_no`,`source_type`)  COMMENT '保证业务唯一性'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='幂等性校验表';

唯一主键UNIQUE KEY的关键性字段如下:

  • serial_no:唯一序列号的值,在分布式架构下是全局唯一的ID
  • source_type:业务类型,区分不同的业务,订单,支付等。

具体处理逻辑如下图所示:

在这里插入图片描述

2.4 状态机实现幂等

对于很多业务是有业务流转状态的,如订单的待提交,待支付,已支付,取消等,在业务逻辑处理的时候只支持状态的单向改变。业务表在设计的时候增加状态字段status,这样在更新的时候加上“status=期望的status”,多次调用的话实际也只会执行一次。

update xx where id=1 and status=1

在这里插入图片描述

3、总结

分布式架构下幂等性是保证接口能够重复执行的重要机制,幂等性和防重又有所不同,防重是在第一次请求已经成功的情况下人为多次重复操作导致的状态改变,幂等性是在不确定第一次请求结果的情况下,发起多次请求不会出现状态的变化。实际使用中,通过数据库主键的唯一性可以实现幂等性和防重,乐观锁的version机制能够实现并发更新下的幂等性,也可以通过数据库悲观锁机制在业务操作前获取锁资源实现唯一性操作。总而言之,分布式接口的幂等性是在牺牲一定的并发和性能的前提下,以实现系统的稳定性和容错性。


参考资料:

  1. https://blog.csdn.net/tengxvincent/article/details/81773745
  2. https://www.cnblogs.com/jajian/p/10926681.html
  3. https://blog.csdn.net/qq_41863849/article/details/123973348
  4. https://zhuanlan.zhihu.com/p/70748661
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

分布式接口幂等性设计实现 的相关文章

随机推荐

  • gdb调试正在运行的进程

    http elisawell blog 163 com blog static 171462881201223061149953 有时会遇到一种很特殊的调试需求 对当前正在运行的其它进程进行调试 正是我今天遇到的情形 这种情况有可能发生在那
  • Python删除指定行数据

    Python小点点儿 按列条件筛选 删除DataFrame的行 遇到清洗数据的问题 需要把某一列中为指定元素的数据 整行去除 尝试了drop却不能到达理想的效果 isin效果理想 import pandas as pd df pd Data
  • Unity使用c#脚本代码编写基于AudioSource的音频淡入淡出渐变效果

    需求分析与类设计 编写能够通过一个函数调节音乐淡入淡出效果的类 使用时将脚本挂载在AudioSource的物体上 其他脚本的物体能够访问物体上的这个类进行音量调节 需要导入的外部成员变量 1 本物体的AudioSource 2 目标音量 实
  • Lua模拟C#的类继承

    写Lua的都知道Lua语言本身是不提供类继承这个概念的 但是我们可以根据Lua提供的设置元方法的特性来模拟类的建立 以下是我写的一个模拟C 类继承的Lua方法 即只能继承一个父类 但可以继承多个接口 我这个模拟构造类时 父类一定要放在第一个
  • 【22】CSS核心样式(3)——盒模型5种属性

    盒模型又叫框模型 包含了五个用来描述盒子位置 尺寸的属性 分别是宽度 width 高度 height 内边距 padding 边框 border 外边距 margin 为了更好理解 如下生活中的举例 常见盒模型区域 盒模型的属性中 根据不同
  • 数据分析流程

    数据分析流程 1 明确分析目的与框架 2 数据收集 3 数据处理 4 数据分析 5 数据展现 6 撰写报告 数据分析流程概括起来主要包括明确分析目的与框架 数据收集 数据处理 数据分析 数据展现和撰写报告6个阶段 1 明确分析目的与框架 明
  • AI厂工什么时候开始赛博搬砖?

    最近两个月 二次元们找到了AI的 正确用法 玩梗 以造梗最多的NovelAI为例 无论你投喂什么图片 AI都能二次元化 输出精美中不失离谱的图片 你猜它们的原图是什么 这只是大量AI作画正面案例里的一个少数 最近两个月 AI作画带着大量梗图
  • 二十四. Kubernetes 安全

    目录 一 一 官方文档 k8s中不管是外部通过ui管理端操作 还是通过命令行 再或者集群内部执行的操作指令 所有指令都会发送给ApiServer 即使是pod也会被集群认为是一个用户 会给这个用户颁发一个ServiceAccount服务账号
  • STM32 BootLoader跳转之前关闭全部中断

    关闭全局中断 DISABLE INT 关闭滴答定时器 复位到默认值 SysTick gt CTRL 0 SysTick gt LOAD 0 SysTick gt VAL 0 设置所有时钟到默认状态 使用HSI时钟 HAL RCC DeIni
  • CSS自己实现一个步骤条

    前言 步骤条是一种用于引导用户按照特定流程完成任务的导航条 在各种分步表单交互场景中广泛应用 例如 在HIS系统 门诊医生站中的接诊场景中 我们就可以使用步骤条来实现 她的执行步骤分别是 门诊病历 gt 遗嘱录入 gt 完成接诊 我们发现
  • 华为OD机试真题 Java 实现【货币单位换算】【2023Q1 100分】

    一 题目描述 记账本上记录了若干条多国货币金额 需要转换成人民币分 fen 汇总后输出每行记录一条金额 金额带有货币单位 格式为数字 单位 可能是单独元 或者单独分 或者元与分的组合要求将这些货币全部换算成人民币分 fen 后进行汇总 汇总
  • 使用docker进行部署hadoop

    使用docker进行部署hadoop 安装docker wget qO https get docker com sh 安装完成后 要启动docker服务 sudo service docker start 查看是否运行成功 ps aux
  • C++——函数指针

    在C 中 函数指针是指向函数的指针变量 它允许将函数作为参数传递给其他函数 动态选择调用的函数以及在运行时改变函数的行为 函数指针的声明和使用如下所示 1 声明函数指针类型 returnType pointerName parameterT
  • 我的一路走来@电子信息工程和嵌入式该怎么入门

    嵌入式该怎么学 嵌入式从何学起 嵌入式入门需不需要报培训机构 哪个培训机构好点 还有一些是咨询电子信息工程专业的情况等等 这些问题几乎每天都在我的 嵌入式的世界 百度知道团队会遇到和看到的一些问题 归根结底是咨询嵌入式该如何入门 电子信息工
  • js中通过window.location.href和document.location.href、document.URL获取当前浏览器的地址的值,它们的的区别

    1 document表示的是一个文档对象 window表示的是一个窗口对象 一个窗口下可以有多个文档对象 所以一个窗口下只有一个window location href 但是可能有多个document URL document locati
  • HTML+CSS字体文本

    声明 本人的所有博客皆为个人笔记 作为个人知识索引使用 因此在叙述上存在逻辑不通顺 跨度大等问题 希望理解 分享出来仅供大家学习翻阅 若有错误希望指出 感谢 HTML文本标签 文本级语义标签包括 a 超连接 em 侧重点的强调 可嵌套 表现
  • Laya实现控制杆控制3D模型旋转

    export default class JoyStick constructor mod this model mod 模型 this scale Laya Browser width 1920 this rockerBtnOrigin
  • uniapp App权限判断和提示

    1 下载组件App权限判断和提示 DCloud 插件市场 2 导出到需要判断的项目里面 import permision from js sdk wa permission permission js 3 判断是否开启权限 async re
  • 图书馆管理系统 Java

    目录 要求 代码 Operate接口 Book类 Reader类 BookList类 ReadList 类 Infor类 InforList类 main 功能实现 改进 错误 总结 要求 为图书管理人员编写一个图书管理系统 图书管理系统的设
  • 分布式接口幂等性设计实现

    面对分布式架构和微服务复杂的系统架构和网络超时服务器异常等带来的系统稳定性问题 分布式接口的幂等性设计显得尤为重要 本文简要介绍了几种分布式接口幂等性设计实现 包括Token去重机制 乐观锁机制 数据库主键和状态机实现等 以加深理解 1 分