如何保证分布式服务接口的幂等性

news2024/11/12 15:23:02

1 问题背景

可能你最先想到的就是使用数据库的事务保证。比如创建订单时,要同时往订单表和订单商品表中插入数据,那这些插入数据的INSERT必须在一个数据库事务中执行,数据库的事务可以确保:执行这些INSERT语句,共赴生死!

假如你有个服务部署在5台机器上,有个付款接口。然后用户在前端操作时,一份订单不小心发起了两次支付请求,然后这俩请求分散在了这个服务部署的不同的机器上,结果一个订单扣款扣两次,gg!

订单系统调用支付系统进行支付,结果不小心网络超时,然后订单系统走了前面我们看到的那个重试机制,给你重试了一把,好,支付系统收到一个支付请求两次,而且因为负载均衡算法落在了不同的机器了! 但这还是有很多大坑存在。一个分布式系统中的某个接口,要保证幂等性,如何保证?

2 如何避免重复下单?

评论里有同学说,前端页面直接防止用户重复提交表单。没啥毛病,但网络错误会导致重传,很多RPC框架、网关都有自动重试机制,所以重复请求无法避免。 所以问题归结于如何保证服务接口的幂等性。

2.1 怎么判断请求是否重复

  1. 插入订单数据前,先查一下订单表里面有没有重复订单? 这可不好啊,因为你很难用SQL的条件来定义“重复的订单”
  2. 订单用户一样、商品一样、价格一样,就是重复订单? 万一这搞笑用户就是连续下了俩一模一样订单呢

2.2 最佳实践

保证幂等性主要有如下几点

每个请求须有唯一标识

比如订单支付请求,得包含订单id,一个订单id最多支付一次

每次处理完请求后,须有记录标识该请求已被处理

比如说常见的方案是在MySQL中记录一个状态字段。比如支付之前记录一条这个订单的支付流水

每次接收请求判断之前是否处理过

若有一个订单已支付,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId已存在,唯一键约束生效,报错插入不进去。就不会再重复扣款。

在往db插条记录时,一般不提供主键,而由数据库在插入时自动生成一个主键。这样重复的请求就会导致插入重复数据。

MySQL的主键自带唯一性约束,若在一条INSERT语句提供主键,且该主键值在表中已存在,则该条INSERT会执行失败。因此可利用db的“主键唯一约束”,在插数据时带上主键,以此实现创建订单接口的幂等性。

给订单服务添加一个“orderId生成”的接口,无参,返回值就是一个全局唯一订单号。在用户进入创建订单页面时,前端页面先调用该orderId生成接口得到一个订单号,在用户提交订单的时候,在创建订单的请求中携带该订单号。

该订单号其实就是订单表的主键,如此一来,重复请求中带的都是同一订单号。订单服务在订单表中插入数据的时候,执行的这些重复INSERT语句中的主键,也都是同一个订单号。而数据库的唯一约束可保证,只有一次INSERT执行成功。

实际要结合业务,比如使用Redis,用orderId作为唯一键。只有成功插入这个支付流水,才可执行扣款。

要求是支付一个订单,必须插入一条支付流水,order_id建立一个唯一键unique key 你在支付一个订单前,先插入一条支付流水,order_id就已经传过去了 你就可以写一个标识到Redis中,set order_id payed,当重复请求过来时,先查Redis的order_id对应的value,若为payed说明已支付,就别重复支付了!

然后再重复支付订单时,写尝试插入一条支付流水,db会报错unique key冲突,整个事务回滚即可。 保存一个是否处理过的标识也可以,服务的不同实例可以一起操作Redis。

  • 幂等创建订单的时序图 alt

如果因为重复订单导致插入订单表失败,订单服务不要把这个错误返回给前端页面. 否则,就可能出现用户点击创建订单按钮后,页面提示创建订单失败,而实际上订单却创建成功了. 正确的做法是,遇到这种情况,订单服务直接返回订单创建成功即可.

3 怎么解决ABA问题?

3.1 什么是 ABA?

比如订单支付后,卖家要发货,发货完成后要填个快递单号。假设说,卖家填了个666,刚填完,发现填错了,赶紧再修改成888。对订单服务来说,这就是2个更新订单的请求。系统异常时666请求到了,单号更成666,接着888请求到了,单号又更新成888,但是666更新成功的响应丢了,调用方没收到成功响应,自动重试,再次发起666请求,单号又被更新成666了,这数据显然就错了.

  • 时序图 alt

3.2 解决方案

通用的解决方案

订单主表增加一列version。每次查询订单的时候,版本号要随着订单数据返回给页面。 页面在更新数据的请求中,把这个版本号作为更新请求的参数,带回给订单更新接口。

订单服务在更新数据的时候,需要比较订单的版本号是否和消息中的一致:

  • 不一致 拒绝更新数据
  • 一致 还需要再更新数据的同时,把版本号+1。“比较版本号、更新数据和版本号+1”,这个过程必须在同一个事务里面执行。
UPDATE orders set tracking_number = 666version = version + 1
WHERE version = 8;

在这条SQL的WHERE条件中,version的值需要页面在更新的时候通过请求传进来。

通过这个版本号,就可以保证,从我打开这条订单记录开始,一直到我更新这条订单记录成功,这个期间没有其他人修改过这条订单数据。因为,如果有其他人修改过,数据库中的版本号就会改变,那我的更新操作就不会执行成功。我只能重新查询新版本的订单数据,然后再尝试更新。

有了这个版本号,前文的ABA即有两个 case

  • 把运单号更新为666的操作成功了,更新为888的请求带着旧版本号,那就会更新失败,页面提示用户更新888失败
  • 第二种情况,666更新成功后,888带着新的版本号,888更新成功。这时候即使重试的666请求再来,因为它和上一条666请求带着相同的版本号,上一条请求更新成功后,这个版本号已经变了,所以重试请求的更新必然失败

无论哪种情况,数据库中的数据与页面上给用户的反馈都是一致的。这样就可以实现幂等更新并且避免了ABA问题

  • 下图展示case1 alt

4 总结

  • 对于创建订单服务来说,可以通过预先生成订单号,然后利用数据库中订单号的唯一约束这个特性,避免重复写入订单,实现创建订单服务的幂等性
  • 对于更新订单服务,可以通过一个版本号机制,每次更新数据前校验版本号,更新数据同时自增版本号,这样的方式,来解决ABA问题,确保更新订单服务的幂等性。

两种幂等的实现方法,就可以保证,无论请求是不是重复,订单表中的数据都是正确的。

实现订单幂等的方法,完全可以套用在其他需要实现幂等的服务中,只需要这个服务操作的数据保存在数据库中,并且有一张带有主键的数据表即可

本文由 mdnice 多平台发布

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

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

相关文章

DDR核心和事务调度程序(DDRC)

DDR Core and Transaction Scheduler (DDRC)是内存管理系统中一个关键组件,它主要负责管理和调度对DDR(Double Data Rate,双倍数据率)内存的读写操作。这个组件对于确保系统能够高效地访问内存至关重要,特别是在处理多…

实验15.多线程调度

简介 实验.多线程调度 内核线程 1.在时钟中断函数中处理中,减少当前线程pcb的tick,tick为0则启动调度2.调度,把当前线程pcb放入就绪对立队尾,把就绪线程队首拿出来执行主要代码 引导 省略内核 list.h #ifndef __LIB_KERNEL_…

【GoodERP更新日志】增加采购发票、销售发票 批量抵扣记账 批量撤销入账 功能

开源项目GoodERP更新-2024年7月29日 本次提交合并增加的功能或解决的问题: 1、增加采购发票、销售发票 批量抵扣记账 批量撤销入账 功能(增加上了批量抵扣记账(会检查发票号、开票日期有没有填写上)、批量撤销入账 两个批量功能…

H616设计时候存在的问题

1.存在大量孤铜的问题: 这种情况是绝对不允许的,但是GBA焊盘打大量的过孔会出现很多这样的孤铜: 解决办法: 像这种出现大量重复焊盘的,用导线连接起来,之后铺铜形成铜皮,再在这个小铜皮上面打…

用frp内网穿透https网站

场景说明 在微信小程序上线测试的时候,自主开发的后端服务在公司局域网,小程序前端在微信公众平台只支持配置https协议的域名来访问服务端。公司一直在使用frp内网穿透工具实现公网访问公司局域网服务,因此,研究如何实现frp代理h…

低代码平台飞书apaas

1. 低代码平台 1.1 概述 低代码是无需编码(0 代码)或通过少量代码就可以快速生成应用程序的开发平台。 通过可视化进行应用程序开发的方法,具有不同经验水平的开发人员可以通过图形化的用户界面,使用拖拽组件和模型驱动的逻辑来…

BIM、数字孪生、可视化一结合,我就知道这大屏效果稳稳的啦

在日常的项目中,经常会用到上述三个方面的能力结合,比如智慧工地、智慧楼宇、智慧园区等项目,本文就分享一批这方面的精彩作品。 BIM(建筑信息模型)、数字孪生和可视化大屏可以结合起来,为建筑行业和工程管…

C语言中的二维数组

文章目录 🍊自我介绍🍊二维数组🍊代码实战 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞关注评论收藏(一键四连)哦~ 🍊自我介绍 Hello,大家好,我是小珑也要变强&…

AI 绘画是否符合当代主流审美?

在当今时代,AI 绘画成为了一个备受关注的热门话题。那么,AI 绘画是否符合当代主流审美呢? AI 绘画有着诸多符合当代审美的特质。它能展现出独特的视觉效果,风格丰富多样,如后现代风格、奇幻风格等等,足以满…

前端必备基础【网络通信】(2024最新版)

Ajax Asynchronous Javascript and XML 的缩写,是使用 JS 发起网络通信的技术统称,具体步骤为: 创建 XMLHttpRequest 实例发出 HTTP 请求接收服务器传回的数据更新网页数据(通常是部分内容,而不是整个网页&#xff09…

B端:导航条就框架提供的默认样式吗?非也,看过来。

导航条不一定必须使用框架提供的默认样式,你可以根据项目需求和设计风格进行自定义。通过使用框架提供的自定义选项、CSS样式覆盖、自行设计或者使用其他UI库或组件,你可以实现独特且符合需求的导航条样式。 下面发一些参考给友友们,可以让设…

请你谈谈:vue的渲染机制(render)- 2举例说明问题

如何在 Vue 的 render 函数中使用 createElement 方法来创建虚拟节点(VNode)。这里是一个稍微整理后的示例,它直接对应于你提供的注释和代码片段,但作为一个完整的 render 函数的一部分,可能位于一个 Vue 组件的 scrip…

关于ITSS认证-IT服务工程师、IT服务项目经理常见问题解答!

TSS,即信息技术服务标准(Information Technology Service Standards,简称ITSS),是一套系统化的信息技术服务规范。 它全面规定了信息技术服务产品及其组成要素,旨在指导标准化的信息技术服务实施&#xff…

企业微信开发智能升级:AIGC技术赋能,打造高效沟通平台

文章目录 一、AIGC在企业微信开发中的核心价值1. 智能化客服体验2. 自动化工作流程3. 个性化内容推荐4. 深度数据分析与洞察 二、使用AIGC进行企业微信开发的实践路径1. 需求分析与场景定义2. 技术选型与平台搭建3. 模型训练与调优4. 接口对接与功能集成5. 测试与优化 《企业微…

css各种使用案例合集(一)

1、文字不换行 场景1&#xff1a;使 div 标签的文字内容不换行 代码示例&#xff1a; <div class"nowrap-div">这是一段很长的文字&#xff0c;我们不会让它换行。</div> .nowrap-div { white-space: nowrap; } 2、步骤条 场景2&#xff1a;特殊样式的…

【模拟电路】电与磁的关系,电感与震荡电路

文章目录 前言电与磁的关系电感及其应用电感的电路符号 震荡电路及其作用震荡电路的画法 总结 前言 在电子技术的世界中&#xff0c;电与磁的关系是理解许多电路和设备工作原理的基础。电与磁不仅在理论上紧密相连&#xff0c;在实际应用中也发挥着重要作用。电感器和震荡电路…

新学年即将到来,IT管理员面临一系列挑战

新学年即将到来&#xff0c;学校的IT管理员们又将迎来一场风暴般的挑战。你知道吗&#xff1f;许多教育机构在管理学生账户和访问权限方面都面临着巨大的困难。随着数字化学习的普及&#xff0c;简化和安全的账户管理需求比以往任何时候都更加迫切。本文将详细探讨学生账户管理…

java使用hutool工具判断ip或者域名是否可用,java使用ping判断ip或者域名是否可用

1.导入hutool工具 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version></dependency>2.复制以下代码直接运行 import cn.hutool.core.net.NetUtil;public class Test {p…

python爬虫的基础知识

1.学习爬虫的好处 提升编程技能&#xff1a;爬虫开发需要掌握编程基础&#xff0c;特别是网络请求、HTML/CSS/JavaScript解析、数据存储和异常处理等技能。通过学习爬虫&#xff0c;你可以巩固和提升你的编程技能&#xff0c;特别是Python等编程语言的应用能力。 数据驱动决策…

LitCTF2024赛后web复现

复现要求&#xff1a;看wp做一遍&#xff0c;自己做一遍&#xff0c;第二天再做一遍。&#xff08;一眼看出来就跳过&#xff09; 目录 [LitCTF 2024]浏览器也能套娃&#xff1f; [LitCTF 2024]一个....池子&#xff1f; [LitCTF 2024]高亮主题(划掉)背景查看器 [LitCTF 2…