(转)接口幂等性

转自:  https://blog.csdn.net/mingwulipo/article/details/91443355  

 

1、接口调用存在的问题

 现如今我们的系统大多拆分为分布式SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用RPC通信或者restful,既然是通信,那么就有可能在服务器处理完毕后返回结果的时候挂掉,这个时候用户端发现很久没有反应,那么就会多次点击按钮,这样请求有多次,那么处理数据的结果是否要统一呢?那是肯定的!尤其在支付场景。

2、什么是接口幂等性

接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。举个最简单的例子,那就是支付,用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...,这就没有保证接口的幂等性
 

3、什么情况下需要保证接口的幂等性

在增删改查4个操作中,尤为注意就是增加或者修改,

A: 查询操作

 查询对于结果是不会有改变的,查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作
B: 删除操作

删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个,在不考虑返回结果的情况下,删除操作也是具有幂等性的)

C: 更新操作

修改在大多场景下结果一样,但是如果是增量修改是需要保证幂等性的,如下例子:

把表中id为XXX的记录的A字段值设置为1,这种操作不管执行多少次都是幂等的
把表中id为XXX的记录的A字段值增加1,这种操作就不是幂等的

D: 新增操作

增加在重复提交的场景下会出现幂等性问题,如以上的支付问题

 

4、那么如何设计接口才能做到幂等呢?

常见的两种实现方案: 1. 通过代码逻辑判断实现 2. 使用token机制实现

下面以支付系统为例,分别对接口的幂等性进行说明与实现

A: 通过代码逻辑判断实现接口幂等性,只能针对一些满足判断的逻辑实现,具有一定局限性

用户购买商品的订单系统与支付系统;订单系统负责记录用户的购买记录已经订单的流转状态(orderStatus),支付系统用于付款,提供如下接口,订单系统与支付系统通过分布式网络交互。

boolean pay(int accountid,BigDecimal amount) //用于付款,扣除用户的

在这里插入图片描述

这种情况下,支付系统已经扣款,但是订单系统因为网络原因,没有获取到确切的结果,因此订单系统需要重试。由上图可见,支付系统并没有做到接口的幂等性,订单系统第一次调用和第二次调用,用户分别被扣了两次钱,不符合幂等性原则(同一个订单,无论是调用了多少次,用户都只会扣款一次)。如果需要支持幂等性,付款接口需要修改为以下接口:

boolean pay(int orderId,int accountId,BigDecimal amount)

在这里插入图片描述

通过orderId来标定订单的唯一性,付款系统只要检测到订单已经支付过,则第二次调用不会扣款而会直接返回结果:

在不同的业务中不同接口需要有不同的幂等性,特别是在分布式系统中,因为网络原因而未能得到确定的结果,往往需要支持接口幂等性。

 

随着分布式系统及微服务的普及,因为网络原因而导致调用系统未能获取到确切的结果从而导致重试,这就需要被调用系统具有幂等性。

例如上文所阐述的支付系统,针对同一个订单保证支付的幂等性,一旦订单的支付状态确定之后,以后的操作都会返回相同的结果,对用户的扣款也只会有一次。这种接口的幂等性,简化到数据层面的操作:

update userAmount set amount = amount - ‘value’ ,paystatus = ‘paid’ where orderId= ‘orderid’ and paystatus = ‘unpay’

其中value是用户要减少的订单,paystatus代表支付状态,paid代表已经支付,unpay代表未支付,orderid是订单号。

 

在上文中提到的订单系统,订单具有自己的状态(orderStatus),订单状态存在一定的流转。订单首先有提交(0),付款中(1),付款成功(2),付款失败(3),简化之后其流转路径如图:

在这里插入图片描述

当orderStatus = 1 时,其前置状态只能是0,也就是说将orderStatus由0->1 是需要幂等性的
update Order set orderStatus = 1 where OrderId = ‘orderid’ and orderStatus = 0
当orderStatus 处于0,1两种状态时,对订单执行0->1 的状态流转操作应该是具有幂等性的。这时候需要在执行update操作之前检测orderStatus是否已经=1,如果已经=1则直接返回true即可。

 

但是如果此时orderStatus = 2,再进行订单状态0->1 时操作就无法成功,但是幂等性是针对同一个请求的,也就是针对同一个requestid保持幂等。这时候再执行

 

update Order set orderStatus = 1 where OrderId = ‘orderid’ and orderStatus = 0
接口会返回失败,系统没有产生修改,如果再发一次,requestid是相同的,对系统同样没有产生修改。

 

B: 使用token机制实现接口幂等性,通用性强的实现方法

 token机制实现步骤: 

 1. 生成全局唯一的token,token放到redis或jvm内存,token会在页面跳转时获取.存放到pageScope中,支付请求提交先获取token

 2. 提交后后台校验token,执行提交逻辑,提交成功同时删除token,生成新的token更新redis ,这样当第一次提交后token更新了,页面再次提交携带的token是已删除的token后台验证会失败不让提交


 token特点:   要申请,一次有效性,可以限流

 注意: redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用。。

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

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

相关文章

高级java必须清楚的概念:原子性、可见性、有序性

转载自 高级java必须清楚的概念:原子性、可见性、有序性 原子性、可见性、有序性是多线程编程中最重要的几个知识点,由于多线程情况复杂,如何让每个线程能看到正确的结果,这是非常重要的。 原子性 原子性是指一个线程的操作是不能…

背包问题总结

参考链接: http://www.cnblogs.com/fengty90/p/3768845.html http://blog.csdn.net/mu399/article/details/7722810 http://blog.csdn.net/xiaowei_cqu/article/details/8191808 http://blog.csdn.net/insistgogo/article/details/11176693 原文:https:…

(转)数据库可靠性/可用性、稳定性RTO/RPO

转 https://blog.csdn.net/luke_wang/article/details/78145517; 在灾难恢复方面,目前业界公认有三个目标值得努力。一是恢复时间,企业能忍受多长时间没有 IT,处于停业状态;二是网络多长时间能够恢复;三是…

HDU2059(DP)

Problem Descrption 据说在很久很久以前,可怜的兔子经历了人生中最大的打击——赛跑输给乌龟后,心中郁闷,发誓要报仇雪恨,于是躲进了杭州下沙某农业园卧薪尝胆潜心修炼,终于练成了绝技,能够毫不休息得以恒…

Java多线程之守护线程实战

转载自 Java多线程之<<守护线程>>实战定义什么是守护线程&#xff1f;与守护线程相对应的就是用户线程&#xff0c;守护线程就是守护用户线程&#xff0c;当用户线程全部执行完结束之后&#xff0c;守护线程才会跟着结束。也就是守护线程必须伴随着用户线程&#x…

转:集群和分布式的区别

转自&#xff1a; https://blog.csdn.net/shuaipu813/article/details/52083289 集群 多台服务器组成的一组计算机&#xff0c;作为一个整体存在&#xff0c;向用户提供一组网络资源&#xff0c;这些单个的服务器就是集群的节点。 集群拥有以下两个特点&#xff1a; 1. 可…

如何quot;优雅quot;地终止一个线程?

转载自 如何"优雅"地终止一个线程&#xff1f;我们的系统肯定有些线程为了保证业务需要是要常驻后台的&#xff0c;一般它们不会自己终止&#xff0c;需要我们通过手动来终止它们。我们知道启动一个线程是start方法&#xff0c;自然有一个对应的终止线程的stop方法&a…

HDU1087

Problem Descrption Nowadays, a kind of chess game called “Super Jumping! Jumping! Jumping!” is very popular in HDU. Maybe you are a good boy, and know little about this game, so I introduce it to you now. The game can be played by two or more than two p…

转-BFF调研-1

转自&#xff1a; https://blog.csdn.net/duola8789/article/details/89332064 前端开发中存在的难问题 多端应用&#xff0c;不同类型客户端对数据、API有个性化的需求服务聚合&#xff0c;单一后端为多个前端团队提供接口&#xff0c;导致跨团队协作低效&#xff0c;资源协…

JDK8新特性之Stream流

转载自 JDK8新特性之Stream流 是什么是Stream流 java.util.stream.Stream Stream流和传统的IO流&#xff0c;它们都叫流&#xff0c;却是两个完全不一样的概念和东西。 流可以简单的说是处理数据集合的东西&#xff0c;可以申明式流式API来处理集合&#xff0c;而不是写一个…

(转)贫血和富血

转自&#xff1a; https://blog.csdn.net/t0404/article/details/51865174 贫血vs富血 我们来回顾一下。在企业架构模式中&#xff0c;业务层的实现一般有两种模式&#xff1a;一种是事务角本模式&#xff08;Transaction script&#xff09;&#xff0c;另一种是领域模型模式…

JDK8的排序大法!!

转载自 屌炸天&#xff0c;JDK8的排序大法&#xff01;&#xff01; 今天总结了下JDK中排序的方法&#xff0c;包括JDK8中强大的lambda表达式及函数式接口运用&#xff0c;不废话&#xff0c;请看下面示例。 public class Test {public static void main(String[] args) {Lis…

康复题11

定义一个二维数组&#xff1a; int maze[5][5] {0, 1, 0, 0, 0,0, 1, 0, 1, 0,0, 0, 0, 0, 0,0, 1, 1, 1, 0,0, 0, 0, 1, 0,}; 它表示一个迷宫&#xff0c;其中的1表示墙壁&#xff0c;0表示可以走的路&#xff0c;只能横着走或竖着走&#xff0c;不能斜着走&#xff0c;要求…

JDK8新特性之函数式接口

转载自 JDK8新特性之函数式接口 什么是函数式接口 先来看看传统的创建线程是怎么写的 Thread t1 new Thread(new Runnable() {Overridepublic void run() {System.out.println("t1");} }); t1.start(); 再来看看使用了函数式接口是怎么写的 Thread t2 new Thre…

hashCode和identityHashCode的区别你知道吗?

转载自 hashCode和identityHashCode的区别你知道吗&#xff1f;hashCode 关于hashCode参考之前的文章&#xff0c;点击参考之前文章。 identityHashCode identityHashCode是System里面提供的本地方法&#xff0c;java.lang.System#identityHashCode。 /*** Returns the same ha…

转:drop、truncate和delete的区别与选择

转自&#xff1a; https://blog.csdn.net/shadow_zed/article/details/78252494 &#xff08;1&#xff09;日志与事务 1.delete语句执行删除的过程是每次从表中删除一行&#xff0c;并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作。 2.truncate tab…

Java高级篇——深入浅出Java类加载机制

转载自 Java高级篇——深入浅出Java类加载机制类加载器简单讲&#xff0c;类加载器ClassLoader的功能就是负责将class文件加载到jvm内存。类加载器分类从虚拟机层面讲分为两大类型的类加载器&#xff0c;一是Bootstrap Classloader即启动类加载器&#xff08;C实现&#xff09;…

康复题21

For a positive integer n lets define a function f: f(n)   - 1  2 - 3  ..  ( - 1)nn Your task is to calculate f(n) for a given integer n. Input The single line contains the positive integer n (1 ≤ n ≤ 1015). Output Print f(n) in a …

转: databasemetadata 无法获取数据库表备注的解决方法

转自&#xff1a; https://blog.csdn.net/10km/article/details/77389038 mysql/jdbc:设置useInformationSchematrue读取表注释信息(table_comment) 问题描述 今天在读取表的注释信息(COMMENT)时,发现返回的REMARKS字段返回居然是null. 以下是代码示例: DatabaseMetaData me…

IntegerCache的妙用和陷阱

转载自 IntegerCache的妙用和陷阱 考虑下面的小程序&#xff0c;你认为会输出为什么结果&#xff1f; public class Test {public static void main(String[] args) {Integer n1 123;Integer n2 123;Integer n3 128;Integer n4 128;System.out.println(n1 n2);System.ou…