8年测试经验,接口测试的幂等性测试分析,看这一篇就够了...


前言

1、什么是接口幂等

概念:接口幂等性就是用户对于同一个接口发起的一次请求或者多次请求的结果是一致的,不会因为多次请求而产生不同的结果。

案例:

用户购买商品后需要进行支付,支付扣款成功,但是返回结果的时侯报网络异常,此时钱已经扣了,用户不知道并再次点击支付按钮,此时会进行第二次扣款,返回结果成功,用户查询余额发现扣了两次款,多扣钱了,流水记录也生成了两条,这其实就没有保证接口的幂等性。

2、增删改查涉及的幂等性问题

查询操作:

select * from user where user_id = 1;

不管执行多少次上面的查询语句,如果数据库记录没有变更的话,
查询结果都是一样的。由此可见,select是天然的幂等操作。

删除操作:

delete from user where user_id = 1;

不管删除多少次,都是把数据删除,在不考虑返回结果的情况下,因此删除操作也是具有幂等性的。

更新操作:

update user set age = age + 1 where user_id = 1;

将age字段进行递增操作,每执行一次,那么age就会加一,
显然每次执行结果都不一样,所以这种情况下update操作就不是幂等操作。

新增操作:

insert into order(pkid,order_id,xx) values (1,'1231736721763762',''');

假设pkid是自增的,如果order_id没有做唯一约束的话,
那么可能导致同一个订单保存多条数据,不具备幂等性;如果order_id做了唯一约束,那么这个新增就是幂等的。

3、哪些情况需要保证接口幂等?

对于业务中需要考虑幂等性的地方一般都是接口的重复请求(常见的业务有:支付、转账、订单提交等等)

重复请求是指同一个请求因为某些原因被多次提交。
导致这个情况会有几种场景。下面列举。

1)前端重复提交
用户在新增页面上快速点击多次,造成发了多次请求,后端重复保存了多条一摸一样的数据。
如用户提交订单,生成很多重复的订单。

2)消息重复消费
消息重复消费,一般是指消息中间件。如RabbitMQ,由于网络抖动,
MQ Broker将消息发送给消费端消费,消费端进行了消费,
在返回ack给MQ Broker时网络中断等原因,导致MQ Broker认为消费端没能正常消费,这时候MQ Broker会重复将这条消息发送给进行消费,如果没有做幂等,就会造成消费端重复消费同一条消息。

3)页面回退再次提交
举个例子,用户购买商品的时候,如果第一次点击下单按钮后,提示下单成功,跳转到下单成功页面,这时候如果用户点击浏览器返回按钮,返回上一个下单页面。重新点击下单按钮,这时候如果没有做幂等的话,也会造成重复下单的问题。

4)微服务互相调用
分布式系统中,服务之间的通信一般都通过RPC或者Feign进行调用,避免网络出小问题,导致此次请求失败,这时这些远程调用,比如feign都会触发重试机制,所以我们也需要保证接口幂等。

4、如何实现接口幂等

1)前端(初级方式实现幂等、不适合高并发场景)

如防止表单重复提交,按钮置灰、隐藏、按钮不可点击等方式。

2)后端-基于唯一索引(初级方式实现幂等、不适合高并发场景)

我们会在插入或者更新前先判断下,当前这个数据数据库中是否已经存在,如果存在则不允许重复插入,不存在则可插入。

插入数据,应该按照唯一索引进行插入,比如订单号,相同的订单号就不可能有两条记录插入,我们在数据库层面防止重复。这个机制是利用了数据库的主键唯一索引的特性,解决了在插入场景时的幂等问题。

建立了一个l唯一序列号是唯一索引,我们在进行业务操作的时候,往这张表插入一条数据,如果后面第二次提交【序列号还是一样,比如订单ID】,发现这张表的序列号已经在第一次插入进去了,那么第二次操作就什么都不进行,直接返回,保证幂等。

3)数据库锁

数据库悲观锁:

指的就是每次操作的时候,先把记录锁定起来,其他人无法操作这条记录

select * from user where user_id = 1 for update;

注意:数据库悲观锁使用时,一般伴随事务一起使用,数据锁定时间可能会很长,需要根据实际情况选用。

案例:当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。

这里以更新商品订单状态来举例:一般订单有订单创建、订单确认、订单支付、订单完成、取消订单等订单流程。
当我更新订单状态为订单完成的时候,我们首先通过判断该订单的状态是否是订单支付,如果不是则直接返回,否则更新状态为已完成。

这是我们常见的一种写法,但这种写法在高并发环境下,可能会造成一个业务被执行两次的情况发生:
同时有两个请求过来,大家几乎同时查数据库订单状态,都是订单支付状态,然后就支持接下来一系列操作,这就导致一个业务被执行了两次,如果接下来一系列操作不是幂等的那么就会出现脏数据。这里我们就可以通过悲观锁实现,也就是添加for update字段。

这里order_no需要添加索引,否则会锁表。
悲观锁在同一事务操作过程中,锁住了一行数据。悲观锁性能不佳所以一般不建议用悲观锁做这个事情。

数据库乐观锁:

就是利用版本号的概念,在操作前先获取到操作记录的当前version版本号,然后操作的时候带上此版本号。

update user set age = age + 1, version = version + 1 where user_id = 2 and version = 1

注意:乐观锁主要使用于处理读多写少的问题。

案例:乐观锁就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制。

所谓的乐观锁就是在表中新增一个version(版本号)字段。
通过版本号的方式,来控制update的操作的幂等性,用户查询出要修改的数据,系统将数据返回给页面,将数据版本号放入隐藏域,用户修改数据,点击提交,将版本号一同提交给后台,后台使用版本号作为更新条件。

注意:乐观锁能够保证的是update的操作的幂等性,如果你的update本身就是幂等操作或者install操作那就不能用乐观锁了。

4)redis set防重

很多数据需要处理,只能被处理一次,比如我们可以计算数据的MD5
将其放入redis的set数据结构,每次处理数据,先看这个MD5是否已经存在,如果已经存在就不处理。

5)基于分布式锁

如果多个线程可能在同一时间处理相同的数据,比如多个线程在同一时刻都拿到了相同的数据处理,我们就可以加分布式锁,锁定此数据,处理完成后释放锁。获取到锁的必须先判断这个数据是否被处理过。

分布式锁实现幂等性的逻辑就是,请求过来时,先去尝试获得分布式锁,如果获得成功,就执行业务逻辑,反之获取失败的话,就舍弃请求直接返回成功。其实前面介绍过的悲观锁,本质是使用了数据库的分布式锁,都是将多个操作打包成一个原子操作,保证幂等。但由于数据库分布式锁的性能不太好,我们可以改用:redis或zookeeper来实现分布式锁。

6)基于唯一索引

一般来讲悲观锁、乐观锁、状态码作用于update操作来实现幂等,而唯一索引是针对insert操作来保证幂等。

创建订单时,前端先通过接口获取订单号,再请求后端时带入订单号,订单表中订单号添加唯一索引,如果存在插入相同订单号则直接报错。

消费MQ消息时,messageId是唯一的,我们可以新添加一种消费记录表,将messageId作为主键,如果重复消费那么就会存在相同的messageId,插入直接报错。

具体流程步骤:
建立一张去重表,其中某个字段需要建立唯一索引
客户端去请求服务端,服务端会将这次请求的一些信息插入这张去重表中
因为表中某个字段带有唯一索引,如果插入成功,证明表中没有这次请求的信息,则执行后续的业务逻辑如果插入失败,则代表已经执行过当前请求,直接返回。

接口测试项目实战

下面是我整理的2025年最全的软件测试工程师学习知识架构体系图

一、Python编程入门到精通

请添加图片描述

二、接口自动化项目实战

请添加图片描述

三、Web自动化项目实战

请添加图片描述

四、App自动化项目实战

请添加图片描述

五、一线大厂简历

请添加图片描述

六、测试开发DevOps体系

请添加图片描述

七、常用自动化测试工具

请添加图片描述

八、JMeter性能测试

请添加图片描述

九、总结(尾部小惊喜)

人生最动人的篇章,往往写在最艰难的转折之后。当你觉得力不从极速赛车开奖历史记录查询心时,请记住:每一个"不可能"的突破,都始于"再试一次"的勇气。你的坚持,正在为世界书写新的可能!

别让他人的质疑成为你的枷锁!你体内蕴藏着改变命运的力量,每个微小的进步都在为辉煌铺路。当别人停下脚步时,你的坚持就是最有力的回应。向前走,属于你的舞台正等待绽放!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值