性能测试中的压力模拟

前言

性能测试利用性能测试工具模拟正常,峰值以及异常负载条件下的被测系统的各项性能指标。性能测试工具可以选择工业上成熟的(如常用的web性能测试工具JMeter,Loadrunner, Rational Robot等,数据库性能测试工具sysbench, super-smack等),或者团队内部根据项目定制开发。一般来说,一个性能测试工具至少要包括压力控制器,压力产生器,性能监控器,性能结果分析器。一个优秀的性能测试工具至少得具备参数可配置化,监测对象可移植性,压力分发可伸缩性。

最近项目上的一个版本发布,主要考虑在不同压力下的性能数据是否合格。性能测试工具由团队内部开发,相对比较成熟。在这之前,虽然对性能测试工具的实现有一定的了解,但没有完整参与过一个真正上线产品的性能测试。而这次由我主负责,制定测试计划,设计测试场景,调研测试方法,与团队一起利用现有的性能测试工具完成性能测试。整个过程中,难点不断的出现和解决,假设不断的推倒和重来。摸爬打滚下来的感受是,对于性能测试,光有理论和工具还远远不行,压力模拟才是难点。本文主要记录下分布式系统环境下,性能测试中的有用的压力模拟方法。

1 深入了解被测对象

简单来说,虽然是性能测试,你得把自己当做开发人员,对于影响性能的节点,深入了解这些功能的来龙去脉,列出影响的模块和被影响的模块,明确组件之间的通信机制。不同的测试对象,关注的性能参数自然也会不太一样。web服务更加关心并发的链接数目,平均响应时间等。数据库服务则更加注重数据的读写速度,磁盘IO等。对象存储服务而言,磁盘的IO读写,网络的数据吞吐量等。

下面就对象存储服务为例:前端用户请求处理服务A,后端数据存储服务B。用户发送请求到A,经过各种处理调度,把数据发送给B存储。实际一个云端对象存储系统架构相当复杂,还有很多的其他组件(如授权组件,用户信息组件,文件信息组件,消息中间件,数据库组件等)。更进一步,我们就存储系统中存储策略稍作了解。

对于一个存储系统来说,存储策略至关重要。作为公有云的标杆AWS。S3(Simple Storage Service)正是其上线的第一个服务,从此开启了AWS得云计算生态。再看看开源的Openstack里的Swift,Swift 2.0带来的最重要的新特性就是存储策略,以用户需求选择不同的存储方案,充分体现软件定义存储的特点。我们公司的存储服务是商业应用,关于存储策略就此略过。感兴趣的朋友可以去深入了解下开源Swift里的存储策略。简单点说,就是以什么样的算法来存储数据(多副本,区域选择,纠删码,性能分层等),从而保证高的数据持久性(Durability), 系统可用性(Availability),系统稳定性(Stability)。

回到A和B,我们拿存储策略中的纠删码为例,假如存储算法是把对象分成m个分片和n个纠删码,存储算法可以根据(m+n)片当中的任何m片来得到原始对象。这中算法对于有部分数据丢失(分片<=n)的情况下,我们可以通过计算得到对象。这种算法对数据编码,解码和恢复数据都需要有大量的计算,使用于性能要求不是太高,但对数据的持久性和可靠性比较高的场合。在了解存储系统的这种存储策略后,我们性能测试中,需要覆盖的是假如B由于网络延时或者磁盘坏道或者其他原因造成数据分片丢失。如果在大面积数据丢失的情况下,会引起A的大量计算来恢复数据。这种情况下,不管是A还是B,相应的资源消耗都会加大,处理能力降低。我们需要性能测试数据化这种影响。

2 压力模拟

所谓的压力,从被测对象角度来说,即服务器需要处理的事务的总和。有来自前端的压力(用户请求,网络等),有来自后端的压力(后端组件,网络等),还有来自自身的压力(比较常见的有参数配置调优)。一般性能测试中,对于来自前端的压力测的比较充分。对于自身压力,一般由开发者,运维和测试共同完成。性能测试当中,对于来自后端的压力,比较容易被忽视掉。对测试人员来说,被测对象就是一个黑盒子。从前端,从用户的角度进行各种压力模拟,用户场景模拟来评估系统的整体性能。

先看个简单的例子:比如一个小型论坛服务器,给出的参数是,支持并发500,响应时间2s。从功能测试的角度,我们需要验证登陆,登出,发帖,回帖等各项基本功能。对性能测试的角度,我们需要模拟500个并发的压力,这并不难。难点是我们如何模拟这五百个并发,我们需要去挖掘线上同时最大(最小,平均)的在线人数数据,最大(最小,平均)发帖人数,在线人数的时间段分布等等。有了这些线上真实用户数据,我们在测试环境下,按照一定的规模构建用户场景来进行压力模拟。

对于功能测试,输入比较单一,测试人员只需要覆盖掉功能点即可。测试环境毕竟不是真实的用户环境,不管是硬件配置,软件配置都跟真实的线上环境有一定的差别。那么如何在测试环境下,尽可能的去评估一个系统的整体性能,这里光有强大的性能测试工具还不行。压力模拟才是关键。

2.1 线上数据挖掘

大数据时代,到处都是人工智能,机器学习,数据挖掘。例如各大电商,通过线上数据挖掘来分析用户行为,预测用户行为,推送用户感兴趣的广告。发现哪些是用户最近比较感兴趣的商品,哪些商品几乎无人问津,根据这些数据挖掘的结果,及时调整产品发布策略等,做到所谓知己知彼,百战百胜。

性能测试中我们需要构建用户场景,尽可能真实的构建用户场景。还以小型论坛服务器500的并发为例,我们是按照什么样的比例来构建这500的并发(多少比例的登录请求(又有多少比例的登录成功请求,多少比例的失败请求),多少比例的登出请求,多少比例的写请求(发帖,平均数据量),多少比例的读请求(看帖,平均数据量))等。当然,除了较为真实的用户场景构建,我们还需要峰值和异常的用户场景构建。500个写(数据量大小)请求,500个读(数据量大小)请求等。

不同应用的数据挖掘点自然不一样,对于云对象存储,我们需要挖掘线上的数据读和写的比例,请求的密集度,最大(最小,平均)并发请求下得数据大小分布。不同的数据大小直接影响着后端存储服务的磁盘读写IO,吞吐量等。

一般来说,对于有一定规模的产品,线上数据量也是个庞然大物。如何对这些大数据进行高效的数据挖掘,除了必备的数据,信息提取算法外,我们可以借助大数据分析平台(Spark, Storm等),日志分析平台(Splunk, logstash等),数据可视化(Tableau等)来提高效率。

2.2 网络压力

对一个产品而言,网络同样分为外部网络(用户与服务之间)和内部网络(组件之间)。网络有延时,丢包,断开等各种情况。一般来说,服务器在网络出现异常的情况下的处理方式也不太一样,消耗的资源(CPU, 内存等)也不一样,例如超时重传对长连接的维护。同样,内部网络出现异常时,会间接的反应到服务器入口的处理速度。我们不能假设产品一直处于一个良好的网络里。

而且线上和测试环境里的网络环境(硬件和软件)也不可能完全一致,例如网卡速度,数据中心的规模等。有时,我们需要把线上环境按照一定的比例缩小到我们测试环境。其中网络压力是必不可少的而且通用的一环。

常用的网络压力有网络延时模拟,丢包模拟,网络限速模拟。由于网络压力造成数据丢失,进一步引起服务组件之间的额外消耗,进而会影响到用户的体验感。

Linux下的网络压力模拟工具常用的有:iptables, tc。简单看两个例子。深入了解请查看iptables和tc的使用手册。

  • 网络丢包模拟

# 随机丢掉30%的udp包
>>iptables -A INPUT -m statistic --mode random --probability 0.3 -p udp -j DROP
>>iptables -A OUTPUT -m statistic --mode random --probability 0.3 -p udp -j DROP

# 网络接口eth0为20%到30%的丢包率
>>tc qdisc change dev eth0 root netem loss 20% 30%
  • 网络延时模拟

# 网络接口eth0上加入3000ms到5000ms的网络延时(3000+1000/3000-1000)
>>tc qdisc add dev eth0 root netem delay 4000ms 1000ms

# 假设组件A和B,A会访问B的2015端口,现在给A访问B的2015端口的请求加入5000ms的延时,在A所在的机器上:
>>tc qdisc add dev eth0 root handle 1: prio
>>tc qdisc add dev eth0 parent 1:3 handle 30: netem  delay 50000ms 50000ms distribution normal
>>tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 match ip dport 2015 0xffff flowid 1:3
  • 网络限速模拟(由于tc操作比较复杂,我们可以用wondershaper(封装tc脚本), trickle。)

# 限制eth0的网络download:1000kbps, upload:200kbps
>>wondershaper eth0 1000 200

2.3 组件仿制(Mock)

所谓组件仿制,就是重写组件的部分功能。看上去组件还是那个组件,只是某些功能跟正常情况下不一样。可以加延时,错误注入等。引入组件仿制的目的是给调用该组件的服务一定的压力模拟。

回到上面的例子A和B,我们可以对B组件的写数据进行仿制,正常情况下是A调用B写数据,B返回A成功或失败。这里我们可以定制B实现这些功能:

  • B写数据时以一定的比例(如20%)的概率会发生写失败

  • B写数据时不返回

  • B写数据时加入一定的延时

  • ...

这种组件之间的压力模拟对于整个系统的鲁棒性测试非常有用。虽然通过第二节的网络模拟也会出现写数据丢失,但网络毕竟不像控制组件那么容易定量化。这样我们可以定量的监测A和B的性能(如CPU/内存/磁盘IO/网络带宽等)在组件B的异常压力模拟下。

可以看到组件仿制,要求比较高,得看组件的实现源代码。然后在相应的地方进行修改来达到我们想要的目的。测试人员毕竟不是组件的开发者,短时间内要定制自己想要的组件还是有不少难度。因此这个时候,我们需要跟开发着密切配合,在最短的时间内找到需要定制功能的入口点做修改。组件仿制的好处也是显而易见的,由于环境不一样,线上的某些现象在测试环境不一定能重现,这个时候,组件仿制间接来达到我们想要的目的。

总结

本文结合项目中性能测试实践,浅谈了下性能测试中的压力模拟的流程和方法:

  • “深入了解被测对象” => 对性能节点进行评估,定量化性能参数输出预估

  • “压力模拟:线上数据挖掘” => 构建真实的用户场景

  • “压力模拟:网络压力” => 适配环境压力(测试环境和线上环境)

  • “压力模拟:组件仿制(Mock)” => 异常压力注入


你可能感兴趣的:(自动化,性能测试)