yangjianxv
作者yangjianxv·2017-06-01 14:41
部门总经理·成方金融科技有限公司

性能测试工具之关键设计

字数 4195阅读 5185评论 0赞 3

在一些企业、一些场景下,通用的性能测试工具(LoadRunner、JMeter、RPT等)并不一定满足性能测试的需要,这时就需要自行开发量身定制的工具。本节介绍自行开发性能测试工具时的一些关键设计,从原理上说明为什么这样的设计在高吞吐情况下可以获得良好的效果。

一、 性能测试工具的特点

批量发送请求、报文,并达到较高的吞吐量
稳定地控制吞吐量

二、 性能测试工具要实现的目标

  1. 满足业务系统不断增长的业务量对性能测试的要求
  2. 单机吞吐量高,并具备横向扩展性
  3. 减少大规模压力机的维护成本
    例如,管理众多压力机方面,需要批量版本升级、更新交易代码、更新agent配置文件、结果回传等方面

三、 总体架构和设计思路

(一) 主控-代理模式

多台压力机并行发起压力,由主控机统一控制

(二) 互相服务

代理提供服务给主控,实现对各压力机的管理(控制命令、版本更新)
主控提供服务给代理,对各压力机实时监控,结果回传

按理说,主控代理模式只需要单向控制、单向服务即可,为什么优秀的性能测试工具采用双向服务的设计?

这是因为,代理机(压力机)在大规模性能测试的时候,往往是需要满负荷运转的,这个时候,可能根本没有时间或者资源理会主控对它的控制,满负荷运行时,主控对代理机的一些指令可能会导致代理机卡死或者吞吐量大幅波动。

所以,单单是“代理提供服务给主控,主控对其进行控制”的方式,不能适应高吞吐量的性能测试场景。通过增加“主控提供服务给代理”,代理机在合适的时机(程序运行到合适的时机)调用主控的服务,进行结果的回传等操作,能够改善性能测试工具的运行效果。

(三) 数据互相独立

各代理之间数据独立
代理与主控直接数据独立

四、 关键设计

之前提到,性能测试工具之所以可以称之为性能测试工具,最主要的两个要素是
1) 可以产生高吞吐量
2) 可以将稳定地控制吞吐量

如果自己写个简单的小程序,多进程发送压力,并不能称之为性能测试工具。因为这样只能发起高吞吐量的请求,但不能稳定地控制吞吐量。本节详细介绍这两个重要要素是如何设计实现的。

(一) 高吞吐量如何实现

1. 可扩展性

高吞吐就意味着大数据量、较高的带宽占用
架构上需要多机并发,并且主控和代理之间,以及各代理之间独立运行,互不干扰。

似乎所有“主控-代理模式”理所应当都应该实现各机器之间独立运行,但实际上,市面上成熟的一些性能测试工具没有做到各机器之间的运行是互相独立的。

以下几类行为均会导致某一台机器成为性能瓶颈,阻碍了吞吐量的横向扩展性,即不能通过增加压力机的方式增加吞吐量。
1)代理机从主控服务器得到待发送的数据
RPT、LoadRunner、JMeter等主流性能测试工具目前提供的管理测试数据的方法都是主控管理,除非定制代码。

2)多台代理机从主控服务器得到唯一的序列号
每个报文、每个请求需要唯一的序列号,采用了主控分配数据的方式,所有压力都落在了主控上,没有办法通过增加代理机来提高TPS

3)多台代理机读取同一个数据库

做到压力测试期间要真正的独立运行,这就要求
1)各代理之间不做数据共享,各自读各自的数据。
可以由主控将“产生请求的代码、请求模板”发送到各个代理机,各代理机将模板调入内存后,只更换ID等要素,之后将报文发出。

2)主控不负责数据分配,Message ID这样的需要全局唯一的数据,也提前分段,而不是靠主控实时分配。
每个代理机维护自己的Message ID序列。将锁竞争的影响降到最低。

2. 提高单机能力

提高单台压力机的能力也是非常重要的。单台能力提高后,就可以减少对压力机数量的需求,不但节省硬件成本,也减少了维护成本。

提高单机能力最核心的思路是:简单,专注

采用init-action-exit设计方法,每个Vuser只执行一次init函数,做N次action循环,一次exit函数。

1.png

1.png

RPT没有init、action、exit当中的init、exit,所有的用户迭代,都需要经历init、action、exit的过程,例如发起一个请求,要做init(要去配置文件中读取初始化参数,创建session、创建连接),然后action(组织报文、参数替换、加签、发送),最后exit(关闭各类资源)。

虽然可以通过程序内部全局变量的方式,优化一些重复创建、重复初始化的过程,但少不了条件判断等过程,看似没有多几行代码,但对于性能的影响是比较大的。

LoadRunner、JMeter等工具都是采用了init-action-exit的设计。用户自行定义交易的init、action、exit方法。

但仅仅采用init-action-exit的设计仍然不足以让你的性能测试工具脱颖而出。事实上,JMeter在我们的实测当中,不算很快的工具。如果你想创造一个更快、吞吐量更高的压力工具,不妨尝试以下的建议:

1)没有图形化界面,专注于后台的任务执行

2)在参数读取、交易迭代执行等方面,直接针对自己的系统定制

3)在监控方面,没有过多的冗余监控,只有我们关注的性能指标。一个交易的发送过程中,监控大概要消耗10ms,对于高吞吐量场景,这个10ms的监控是非常消耗资源的,会明显拖慢执行速度的,要知道,性能工具发送一个交易,交易本身也只有10ms左右,加上监控的话就是10+10=20ms才能完成一个真正的交易,而我们把监控的时间省下来,就可以达到吞吐量翻倍的效果。

4)运行期间不受主控干扰(除非是“停止运行”等控制命令),对控制命令的反应略迟钝(例如5秒钟才判断一次是否有控制命令抵达),压力机全速发送测试数据。我们通常理解的控制类命令是很小的,程序接收并处理控制命令并消耗不了多少资源。然而,对于高吞吐量的压力测试来说,这一点点的干扰,也会造成吞吐量的波动。试想,一个进程上CPU执行,需要将自己的数据从内存中带到CPU的cache中,如果你本身就是大数据量、高负载的测试,偶尔冒出来一个小进程要占用10ms的CPU,看似对测试影响不大,但这个小进程要把自己的数据从内存拉到CPU cache,而此时cache没有空间,需要把之前性能测试的数据扔出去,下次测试进程回来的时候,还需要从内存搬回来。这一来一回,就干扰了测试进程的全速运行。

(二) 稳定的TPS如何实现

1. 为什么要稳定

性能测试往往是两个场景做对比。这两个场景之间只变化一个参数,例如:两次测试之间只有版本不同、或只有某个应用参数不同。

变化了这个参数之后,可以查看:相同服务能力(如:吞吐量)的情况下,资源利用率(如CPU利用率)的差异;或者相同资源利用率的情况下,服务能力的差异

而资源利用率是我们是控制不住的,不能保证两次测试CPU利用率相同(系统资源是操作系统调度的,我们只能施加影响,但不能完全控制)

而吞吐量是我们可以控制的,控制测试压力机的报文发送速度。只要压力机发出的报文吞吐量在被测系统的能力范围之内即可。

仅通过开启不同数量的进程/线程的方法,是不能控制吞吐量的。

2. 稳定吞吐量的实现方法

控制TPS,往快控制比较难,但往慢控制还是比较容易的。(如果需要高于TPS,则需要多进程/线程,或者多台压力机来实现。)

通常的想法是:每一次任务之间采用sleep来控制。比如每发送一个报文sleep一次。比如每秒发送一个报文,那么sleep设置1s。每秒发送2个报文,sleep设置0.5s。你会发现:当sleep时间比较短的时候(比如设置sleep 33ms,预期每秒钟发出30笔报文),但实际的TPS和预期的不太一致了。

改进1:

第一:发送报文的action本身是是有时间消耗的,需要把这一部分时间计算在内。如果发送报文的action本身需要20ms,那么sleep设置为10ms,相当于每个action是20+10=30ms的间隔。

假设每秒需要发送10笔报文,那么这个进程需要每个action是1000/10=100ms的间隔。

11.png

11.png

经过上述的处理方法后,实际TPS还是小于预期结果。所以需要继续改进。

改进2:采用随机减少一定的毫秒数的算法,做到TPS的稳定。

为什么用这种算法,需要从操作系统的进程调度讲起

进程调度本质上是操作系统如何合理调度进程上CPU执行。
对于多进程的系统,CPU采用分片的方式把一个一个时间片(也就是CPU的时间段)采用共享的方式分配给进程使用
不同机型、OS的时间片长度不一样,一般默认是10ms,但可以调整

除非将进程绑定在某个CPU上,否则,进程不可能实时的占有CPU资源

图片1

时间片分配.png

时间片分配.png

图片2

进程调度.png

进程调度.png

至于进程什么时候能得到CPU资源、需要等待多久才能得到CPU资源,都是未知的。是根据当时的系统运行状态来决定的。

比如在就绪队列中,前面有多少个进程在等待CPU,前面的进程是把CPU时间片用满还是只用一部分(比如执行到3ms的时候,任务已经执行完毕,或者需要读IO,转为等待状态)

实际上,进程执行时间是这样的

时间片分配2.png

时间片分配2.png

进程在得到CPU之前会等待一个随机的时间。
也就是说,你的进程在得到响应之前,真正执行代码之前会等待一个随机时间。

因此我们的性能测试工具在执行每个action之前,减掉一个随机时间RamdomTime。

还是上面例子

2.png

2.png

不同的机型、不同的进程数配置、不同的吞吐量情况下,进程等待的时间会有所不同,因此可以根据实际情况调节

为什么是减掉随机时间:
假如说,进程得到CPU之前等待了一个随机时间,那么我们减掉一个固定值(即随机时间的平均值)也可以达到缩短的目的。
但由于性能工具一般都是并发多进程/线程的,CPU不可能同时处理那么多任务程,因此把这些任务错开时间启动,会有比较好的效果。即减掉随机时间

当每次迭代(例如:发送报文)之间的间隔时间非常短(比如20ms)的时候,控制的TPS非常不稳定.
原因:假设发送一个报文要15ms,如果设置迭代间隔为20ms,那么sleep 20-15=5ms是非常不准的,很可能被延后几十毫秒才能运行,这样误差的范围就非常大了。并且很难通过上述减掉一个随机数的方式来解决。减掉一个随机数,至少是一个时间片*(0-1之间的一个随机数),即至少减去0-10ms。如果在5ms的基础上做减法,是达不到预期效果的。

微信公众号:性能测试与调优

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

3

添加新评论0 条评论

Ctrl+Enter 发表

本文隶属于专栏

作者其他文章

X社区推广