接口并发测试 常见的并发问题

465 阅读16分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

接口并发测试

常见的并发问题

当下流行一种时尚的软件设计理念“微服务”,把复杂功能组合拆分成若干个独立的服务进行开发,然后有选择地组合执行各服务。微服务开发框架更有利于并发测试设计,每个服务都是测试切入口,可以单独执行。测试切入口越多,越有利于测试场景的设计,有效地执行并发用例。测试切入口从三个方面查找统计:客户端操作、系统间接口调用、内置的定时任务。

  • ·客户端可以使用工具捕获提交到服务器的请求,分析链接、参数进行测试。

  • ·系统接口可以查相关接口文档,开发并模拟其他系统功能进行测试。

  • ·定时任务视开发框架,可能需要二次开发,以接口形式进行测试。

并发测出的问题是一种综合症,往往由多种错误交织在一起,切不可头痛医头脚痛医脚。解决这类问题通常分为以下5个步骤。

(1)通过并发测试找到故障点。

(2)以故障点的现象分析问题原因。

(3)确定产生原因后讨论解决方案。

(4)根据解决方案实施修复。

(5)通过并发测试验证修复情况。

在团队内进行专业的分析讨论,得出结论,是一种硬实力。除了开发以外,希望测试人员也要多多参与技术讨论,提升自身发现定位问题的能力。接下来先了解下因为容易被忽视而导致的常见并发问题。

事务并发的问题

由于事务处理而导致的并发问题,测试人员需要先理解事务是什么意思。举个例子来阐述事务的概念。

故事场景:开心用手机通过定电影票系统购买电影票,找到自己喜欢的电影,选择自己想坐的位置,确认后点击下方的“确认选座”,进入订单支付画面。随后开心进入了订单支付画面,勾上了满减活动、现金抵用券,选择使用支付宝去支付,支付成功后收到平台短信。

结合以上场景,先讨论下系统内部的事务控制。事务的控制好坏往往取决于程序员们的开发技术能力、业务理解能力、专注程度,由于这类错误而导致的bug是非常低级且严重的。

将故事场景进行以下切分。

·故事场景的上半部分:开心用手机购买电影票,找到自己喜欢的电影,选择自己想坐的位置,确认后点击下方的“确认选座”,进入订单支付画面。

“确认选座”与“生成订单”对于定电影票系统是内部接口。

将“确认选座”与“生成订单”定义为一个事务,有以下四个特性。

(1)原子性的操作:要么都做,要么都不做。

(2)状态保持一致性:系统锁定座位时必须生成订单,取消订单时必须解锁座位。

(3)与其他事务互相隔离,不被干扰:座位被其他人选定、操作日志记录失败、断网等。(4)事务提交后永久存在,不会受到任何故障影响。只能另起一个新事务去修改已存在的数据。这个例子的事务比较简单,想象下违反这些特性可能出现的问题。而这些问题就是测试人员在对“确认选座”与“生成订单”接口进行并发测试时,所需要考虑的测试用例覆盖点。

(1)一个座位被多个账号同时锁定,生成了订单。

(2)座位锁定成功,但是订单没生成。

(3)订单取消成功,座位解锁失败。

(4)生成重复的订单号。

(5)操作日志没有完整地记录所有行为。

·故事场景的下半部分:开心进入了订单支付画面,勾上了满减活动、现金抵用券,选择使用支付宝去支付,支付成功后收到平台短信。

“支付成功”对于定电影票系统是外部接口。对于外部接口的事务控制,就需要考虑两个系统的设计。在这里我们假设支付机构接口是成熟、稳定、无问题的。考虑针对订电影票系统的支付接口的事务控制就是外部接口测试的重点。

对支付接口进行并发接口测试,要考虑的事务问题如下。

(1)同一笔订单,不能同时选择多种方式,不能进行多次支付。

(2)重复通知商城支付结果(支付成功、支付超时),只能处理一次订单。

(3)日志记录完整记录发送、接受的支付信息,与测试用例内容相匹配。9.1.2 极限值并发的问题

由于极限值而导致的并发问题,先举个例子来阐述极限值的概念。

故事场景:最近开心测试团队接到一个周年庆营销活动项目的测试任务。营销活动的具体安排为每日9:00—21:00给予每个用户一份惊喜。每人每日可获得两次抽奖机会,会员可通过转发抽奖活动至朋友圈额外获得一次抽奖机会。已经获得一等奖或二等奖的用户不可再获得三四等奖;中奖概率按预估概率进行设定,如果已中奖数量达到每日设定奖品数量上限,该奖项停止。

周年庆营销活动的具体安排就是测试需求,开心将营销活动的具体安排进行拆分,得出并发测试场景。

(1)每日9:00—21:00给予每个用户一份惊喜。

(2)每人每日可获得两次抽奖机会,

(3)会员可通过转发抽奖活动至朋友圈额外获得一次抽奖机会。

(4)已经获得一等奖或二等奖的用户不可再获得三四等奖。

(5)中奖概率按预估概率进行设定。

(6)已中数量达到每日设定奖品数量上限,该奖项停止。

这个场景中处处都是极限值,让我们一起来字字品味,理清测试的切入口。

在这个场景中,先分析测试对象分别有:活动时间、抽奖次数、中奖概率、奖品数量上限、中奖规则。

针对这些对象结合测试场景。分析这些测试对象如果“越界”可能导致的问题作为并发测试的用例覆盖点。

(1)测试活动:不在活动时间范围内,也能参与抽奖。

(2)抽奖次数:活动分享至朋友圈,抽奖机会增加次数超过一次。

(3)抽奖次数:每日抽奖次数超过上限。

(4)中奖概率:中奖概率分配不足100%,或超过100%。

(5)中奖概率:设置中奖概率有效的小数位数。

(6)奖品数量上限:奖项数量上限控制。

(7)中奖规则:已中一等奖或二等奖的用户,是否还能中奖。

压力并发的问题

由于压力负载而导致的并发问题,此类问题可以归类于性能问题。

在此类中,并发测试等同于性能测试,通常被称为压力测试,它是为了了解系统能提供的最 大服务级别,获知系统响应时间、错误率等指标。通过增加并发数实现压力并发的测试行为,除 了能发现系统中的性能问题,更是为了能发现系统功能上的缺陷。

关于此类并发测试需考虑的测试点,希望测试人员要明白数据处理的本质。我从数据库对于 事务处理的角度进行分析,需要测试人员先掌握数据库事务隔离级别的知识。

事务的概念在事务并发问题中已经说明。在理解事务含义的基础上,再看数据库事务的隔离级别。 数据库事务的隔离级别有4个,由低到高依次如下。

1.Read uncommitted(未授权读取、读未提交)

如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。

该隔离级别避免了更新丢失,却可能出现脏读,也就是说事务B读取到了事务A未提交的数据。

2.Read committed(授权读取、读提交)

读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。

该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接着更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。

3.Repeatable read(可重复读取)

读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。

该隔离级别避免了不可重复读取和脏读,但是有时可能出现幻读。这可以通过“共享读锁”和“排他写锁”实现。

4. Serializable(序列化)

提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。

序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用。在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。

这4个级别可以逐个解决脏读、

不可重复读、幻读这3类问题。以下解释这3类情况的含义。

1.脏读

一个事务读取到了另一个事务未提交的数据操作结果。2.更新丢失

更新丢失包括以下两种情况。(1)回滚丢失

当2个事务更新相同的数据源时,如果第一个事务被提交,而另外一个事务却被撤销,那么会连同第一个事务所做的更新也被撤销,也就是说第一个事务做的更新丢失了。

(2)覆盖丢失

当2个或多个事务查询同样的记录然后各自基于最初的查询结果更新该行时,会造成覆盖丢失,因为每个事务都不知道其他事务的存在,最后一个事务对记录做的修改将覆盖其他事务对该记录做的已提交的更新。

3.不可重复读(Non-repeatable Reads)

一个事务对同一行数据重复读取两次,但是却得到了不同的结果,包括以下情况。

(1)虚读:事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。

(2)幻读:事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这是由在两次查询过程中有另外一个事务插入数据造成的。

通常数据库设置默认隔离级别为Read committed(授权读取、读提交),仅支持使用事务并防止脏读。隔离级别越高,越能保证数据的完整性和一致性,但同时对并发性能的影响也会非常越大。当项目需求对系统性能有要求时,程序员就不能通过提高数据库设置级别保证并发事务处理的正确性。如果事务处理不正确,会直接导致功能出错,如资金计算错误等。提高隔离级别导致数据库访问效率急剧下降,为了保证系统性能不受影响,大部分的业务隔离将由程序逻辑进行处理。

下面以故事的形式,介绍测试人员在处理该并发测试需求时要关注的测试用例覆盖点。最常见的覆盖点就是第二类更新丢失(覆盖丢失)产生的问题。

故事场景如下。

开心在网上开店卖鞋子,每时每刻库存都发生着变化:开心上架补货,开心下架撤货,买家下单买鞋,买家撤单取消。

考虑库存情况,可以根据以上场景建立一个基础公式,来校验测试结果是否正确。

剩余鞋子数量=上架数量—下架数量—成功下单数量

再进一步,建立公式二。

剩余鞋子数量=上架的数量—下架数量—下单数量+撤单数量

这些相互对立、交叉的操作都会影响到商品库存,任意两种组合或几种组合,可能打破公式平衡。例如,两个用户同时买一款鞋子,两个都下单成功。但第二个用户突然发现另一款鞋子更适合,她要求退款处理(减1后加1操作)。这样就导致与退款处理同时产生的事务计算出现问题,计算剩余鞋子数量与实际剩余鞋子数量不符,引发资金对账不平等情况。这种情况测试人员可以通过直接读取数据库值,再通过计算公式验证测试结果是否满足预期结果。

其次,在剩余鞋子数量发生变化的同时,测试人员要确定客户看到的情况是否与事务处理结果相符。此类问题可以通过校验买卖过程中的异常情况进行测试。例如,测试场景为:用户在客户端买了鞋子,但前端反馈由于网络问题购买失败,测试人员需要通过检查请求成功或失败的结果数量判定是否与预期结果相符。

同时通过公式记录每次请求后的商品数量变化,将其与前端显示的相关数量作对比。

下架数量=下架请求总数量—下架请求失败数量

说到测试人员判定请求是否成功,要提到另外一个概念:同步请求与异步请求。这两者的区别会造成测试人员判定请求状态与实际请求状态有差异,以至测试结果出错。

举个例子说明什么是同步请求和异步请求。

上班日中午天气太热,开心不想出办公室,让同事帮忙买一下比目鱼盖饭。同事塞着耳机没反应,开心拍拍他继续说,同事听到后不答应。于是开心提出请同事吃比目鱼盖饭,同事欢快地答应出门了。结果比目鱼餐厅今天不营业,同事空手而归。

这个例子有3次请求失败情况,同步请求失败2次,异步请求失败1次。

(1)同步请求:由于同事塞着耳机,环境太吵,根本没听到开心的请求。(网络异常,没收到)(2)同步请求:同事听到了请求,但是没答应。(参数异常,条件不对,拒绝执行)

(3)异步请求:同事没买到饭空手而归。(执行过程中发现资源冲突,执行失败)同步请求:开心等待同事的回答,期间未做其他事情一直是等待回应状态。

异步请求:开心得到同事的第一次回答,过了一段时间后得到第二次回答。此期间开心可以做其他事情。

如果是异步请求,可能会出现两种情况。

(1)即使测试人员在第一次得到“好的”的状态,也不能说明这个请求是成功的。因为“好的”并不说明“饭真的买回来”了。

(2)即使测试人员在第一次得到“失败”的状态,也不能说明这个请求是失败的。因为“失败”可能是由于网络异常造成的没收到回复,实际同事“已经买到了比目鱼饭!”开心可以欢快地吃饭了。

说完以上内容,总结一下测试人员在处理此类并发测试时需要重点考虑以下几点。(1)先确定请求是同步,还是异步。

(2)测试场景中的异常结果前后端处理数据是否相符。(3)并发产生的数据运算是否正确。

异常数据干扰并发的问题

异常数据导致的并发问题,除了并发测试外,测试人员可以通过另一种测试发现问题。对于此类情况的异常数据测试也可以称为系统健壮性测试。此类测试的重点是要根据业务逻辑或系统相关的配置情况构建能够造成异常的测试数据,要求这些数据不能被当作正常数据处理,也不能影响其他正常数据。

例如,测试人员构建测试场景为不断触发定时批处理任务,如果程序员在代码中忽视对异常数据逻辑处理,就会造成数据库连接池爆满、内存溢出、遇到异常数据直接报错中断(待执行任务队列越积越多)等问题。

此类并发测试关注点不是同步并发,而是逐步加压的并发数量,需要测试人员对系统架构配置及数据流逻辑具有非常清晰的认识,才能构建符合测试需求的异常测试场景。