4.浪费时间(Wasted Time)
在使用事务的时候,经常会看到在事务日志中有Wasted Time。Wasted Time是指事务中应该扣除的由于其他原因导致的时间浪费。在默认情况下LoadRunner会将自身脚本运行浪费的时间自动记入Wasted Time。例如,执行关联、检查点等函数的时间。
从LR 11开始只要事务中包含请求便会自动判断自身浪费的时间,这也是为什么在添加事务后我们会看到Wasted Time出现,而在以前的版本不会出现。
除了脚本自身浪费的时间,某些时候使用C语言等外部接口进行处理所消耗的时间也会影响事务的时间,而这个时间LoadRunner无法处理,在这种情况下就需要人为地计算第三方时间开销,并且将这个开销的时间记入Wasted Time中。
运行一下下面的代码:
Action() { int i; int baseIter = 100; char dude[1000]; merc_timer_handle_t timer; // Examine the total elapsed time of the action //Start transaction lr_start_transaction("Demo"); timer=lr_start_timer(); for (i=0;i<=baseIter*1000;i++) { sprintf(dude,"This is the way we waste time in a script = %d", i); } wasteTime=lr_end_timer(timer); lr_wasted_time(wasteTime*1000); lr_end_transaction("Demo", LR_AUTO); return 0; } |
其中,lr_start_timer()是一个LoadRunner自带的时间计数器,它与lr_end_timer()相对应,能够返回这两个函数间的时间差(timer函数的误差一般在4/1000秒左右)。
运行脚本后,等待一段时间脚本运行结束,可以看到以下日志:
Action.c(18): Notify: Transaction "Demo" started.
Action.c(27): wasted time is 85.860000
Action.c(28): Notify: Transaction "Demo" ended with "Pass" status (Duration: 85.8772 Wasted Time: 85.8600).
通过上面这个日志可以看到,在VuGen运行脚本的时候这个100*1000次循环的C语言操作所消耗的时间会被算在Transaction时间内,导致Transaction的时间变长。当通过lr_start_timer计时函数将这个消耗时间加入Wasted Time后,这个脚本就能正确地计算出事务的时间和该事务时间的Wasted Time。当在场景中运行的时候,事务的响应时间会自动扣除Wasted Time。
为了确保响应时间的正确,需要扣除在运行脚本时自身的时间消耗,事务中尽量避免出现非请求的处理内容,如果无法避免可使用lr_wasted_time函数将多余的时间开销扣除。
例如,这样的脚本:
merc_timer_handle_t timer; //变量声明
lr_start_transaction("Demo");
timer=lr_start_timer();
lr_load_dll("getkey.dll");
lr_save_string(getrandkey(),"key");
//通过调用dll获得密钥
wasteTime=lr_end_timer(timer);
lr_wasted_time(wasteTime*1000);
lr_end_transaction("Demo", LR_AUTO);
计算密钥是很消耗时间的,那么可以使用timer这个变量来记录计算的时间,并将这个时间从整个事务中扣除。
在计算Wasted Time时不要直接使用lr_wasted_time覆盖,而忘了加上脚本中LoadRunner函数的自身时间。通过lr_get_transaction_wasted_time()函数可以获得事务自身的Wasted Time,将这个时间累加上第三方统计的Wasted Time再通过lr_wasted_time()函数覆盖。
A.9.4 手工事务
前面都是使用LR_AUTO来自动判断事务状态,现在来做一个脚本,看看LR的事务是如何自动判断状态的。
录制一个论坛注册用户的脚本,在提交注册表单处添加事务开始及结束标志,然后回放该脚本。事务的结果是PASS还是FAIL呢?虽然回放脚本注册用户是失败的(该用户已经存在),但是事务还是在PASS状态下完成了,而且会发现事务的持续时间很短。正常情况下注册一个用户到刷新首页一般都要2秒,现在只需要1秒不到。这是因为当服务器判断到该用户已存在后,就没有了数据插入和刷新首页的操作,而是直接返回错误提示页面。这个1秒是系统处理错误和刷新错误提示页面的时间而不是注册用户刷新回首页所需要的时间。
LR_AUTO也是根据服务器的返回状态信息来决定事务是以LR_PASS状态通过还是以LR_FAIL状态结束的,只要服务器返回页面,那么事务就会认为请求成功发出去了,服务器看懂了请求也返回了内容,自然事务是PASS状态。
这样由于事务自动判断的错误,导致虽然操作是失败的,但得到了一个响应时间,并且这个响应时间又没有正确反映出做这件事情的真正时间,最终就会影响到性能测试得到的数据。
记得在论坛上就有朋友问过这样的问题,为什么系统在用户越来越多的情况下,响应时间不增反减?这种现象很有可能就是没有使用手工事务导致的结果。
对于这种情况就需要手工来判断操作是否成功,通过web_reg_find()检查点函数来检查页面是否返回正确,然后通过rowcount的参数值来进行事务状态判断,做到智能判断事务结果。
例如,检查点函数的rowcount保存在参数loginst中,那么事务的状态就应该这样判断:
lr_start_transaction("login");
web_reg_find("Search=Body",
"SaveCount=loginst",
"Text=登录失败",
LAST);
//登录请求
If(atoi(lr_eval_string("{loginst}"))>=1)
lr_end_transaction("login", LR_FAIL);
else
lr_end_transaction("login",LR_PASS);
通过检查点来检查登录后页面是不是存在"登录失败"这样的内容,如果存在那么loginst的值就大于等于1,然后把loginst的值取出来和1做比较,如果大于1那么就是登录失败,否则就是登录成功。
参数不能与值做比较,所以要先通过lr_eval_string函数将其转化成字符串,然后再通过atoi函数转化成整数,这样才能和整数1作比较。
在绝大多数情况下对于事务都需要采用手工事务的方式来确保事务的正确性和事务时间的有效性。
有些操作会产生不同的服务器返回,而这些返回都代表成功操作,那么在使用手工事务检查点的时候就会比较麻烦。处理这种问题的方法主要有以下几种:
针对可能出现的不同成功结果,从输入用例控制避免出现该情况。(不同的输入会带来不同的业务处理规则,响应时间会有很大的区别,这里应该直接将这两种情况变成两套脚本来实施。)
使用关联函数来获得返回的内容,再通过多分支的方式完成事务状态的确定。
使用反向方法来判断,通过检查错误的内容,从而反向确定成功的情况。
思考题:
对于Phpwind论坛来说,如何做一个有效的用户注册脚本,通过手工事务并且获得准确注册操作的响应时间?
业务分析:
注册用户后,在系统的页面上会出现"完成注册"的信息,可以在注册后返回的页面中检查是否出现了这样的内容来判断注册事务是否成功。
所以在检查点函数中需要添加这个内容,最后可以得到下面的代码:
Action() { web_url("phpwind85", "URL=http://localhost:8000/phpwind85/", "TargetFrame=", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t4.inf", "Mode=HTML", LAST); web_url("注册一个", "URL=http://localhost:8000/phpwind85/register.php", "TargetFrame=", "Resource=0", "RecContentType=text/html", "Referer=http://localhost:8000/phpwind85/", "Snapshot=t5.inf", "Mode=HTML", LAST); web_reg_find("Search=Body", "SaveCount=regst", "Text=完成注册", LAST); lr_start_transaction("regnewuser"); web_submit_data("register.php_3", "Action=http://localhost:8000/phpwind85/register.php·", "Method=POST", "TargetFrame=", "RecContentType=text/html", "Referer=http://localhost:8000/phpwind85/register.php", "Snapshot=t8.inf", "Mode=HTML", ITEMDATA, "Name=forward", "Value=", ENDITEM, "Name=step", "Value=2", ENDITEM, "Name=_hexie", "Value=4291dd3b", ENDITEM, "Name=regname", "Value=chen3", ENDITEM, "Name=regpwd", "Value=111111", ENDITEM, "Name=regpwdrepeat", "Value=111111", ENDITEM, "Name=regemail", "Value=chen3@gmail.com", ENDITEM, "Name=apartment", "Value=130101", ENDITEM, "Name=rgpermit", "Value=1", ENDITEM, LAST); if(atoi(lr_eval_string("{regst}"))>=1) lr_end_transaction("regnewuser", LR_PASS); else lr_end_transaction("regnewuser",LR_FAIL); return 0; } |
一般情况下都可以十分简便地使用手工事务来获得请求的相应时间,但是对于下载操作来说就并不是那么方便了,因为我们没有有效的判断方式来确认下载有效,这个时候需要利用一个web_get_int_property函数来解决。例如,想获得一个带判定的下载操作响应时间,可以这样写:
Action()
{
int i;
lr_start_transaction("download");
//下载操作的请求
web_url("LoadRunner","URL=http://localhost:8000/dl.jpg",LAST);
i=web_get_int_property(HTTP_INFO_DOWNLOAD_SIZE);
lr_output_message("%d",i);
if(i==439182)//当前一个请求的字节数等于439182字节时认为下载成功,否则失败
lr_end_transaction("download",LR_PASS);
else
lr_end_transaction("download",LR_FAIL);
}
观察日志可以发现:
Action.c(8): web_url("download") was successful, 438810 body bytes, 372 header bytes [MsgId: MMSG-26386]
Action.c(9): web_get_int_property was successful [MsgId: MMSG-26392]
Action.c(10): 439182
变量i的值为439182正好等于download请求中body部分的438810加上header部分的372,通过这个值可以确认最后下载的内容是否完成。一般来说对于下载操作的性能测试集中在需要多少时间弹出下载对话框,而后面的内容其实属于网络带宽的问题。另一方面现在很少使用IE进行直接下载,而采用迅雷一类的P2P下载工具,所以很少考虑具体下载文件操作对网络和服务器读写操作的影响。一般在进行下载测试中建议将被下载文件大小设置为0,这样得到的响应时间就是服务器弹出下载框的响应时间。如果需要测试大量下载操作对服务器磁盘及网络的负载,可以一边运行这个测试弹出对话框下载的零附件大小脚本同时负载另外一套下载大附件的脚本,来获得满带宽时下载整体所消耗的时间(当模拟下载大文件时,一般网络带宽和负载机磁盘写非常容易成为瓶颈)。
A.10 集合点
集合点函数可以帮助我们生成有效可控的并发操作。虽然在Controller中多用户负载的Vuser是一起开始运行脚本的,但是由于计算机的串行处理机制,脚本的运行随着时间的推移,并不能完全达到同步。这时需要手工的方式让用户在同一时间点上进行操作来测试系统并发处理的能力,而集合点函数就能实现这个功能。集合点只需要在脚本中插入lr_rendezvous()函数即可。打开Insert菜单下的Rendezvous选项,如图A.143所示。
在弹出的对话框中输入集合点名称run,确定后即可得到对应的脚本:
lr_rendezvous("run");
引号内的就是集合点名称,当脚本在多用户运行的情况下,每次运行到这个函数都会查看一下集合点的策略来决定是等待还是继续运行。集合点的设置内容存放在场景的设置中,当脚本中有集合点函数时,场景中的集合点设置功能就可以访问,如图A.144所示。
图A.143 添加集合点函数 图A.144 场景中的集合点设置
打开场景菜单下的集合点后,可以为集合点进行设置,包括哪些用户使用该集合点、集合点是否有效等,如图A.145所示。
如果脚本中没有集合点,那么场景中的Scenario/Rendezvous集合点功能将会是灰色显示。
集合点策略用来设置虚拟用户集合的方式,打开Policy对话框,如图A.146所示。
图A.145 场景中的集合点设置窗口 图A.146 场景中的集合点策略
集合点提供了以下3种策略。
策略1:当百分之多少的用户到达集合点时脚本继续。
策略2:当百分之多少的运行用户到达集合点时脚本继续。
策略3:多少个用户到达集合点时脚本继续。
这3个策略的区别在于:假设脚本由100个用户来运行,但100个用户并不是一开始就共同运行的。假设每隔1分钟添加10个用户,也就是说10分钟后系统才有100个在线用户。这里100就是指系统访问的所有用户数,而不同时间的在线用户数是不同的。设置的集合点策略百分比均为100%。
在场景运行时,当Vuser脚本运行到集合点函数时,该虚拟用户会进入集合点状态直到集合点策略满足后才释放。
策略1是指当全部用户都运行到了集合点函数才释放集合,让这100个用户并发运行后面的脚本。
策略2是指当前时间如果只有10个用户在线,那么只需要这10个用户都运行到了集合点函数就释放集合,让这10个用户并发运行后面的脚本。
策略3就比较好理解了,当到达集合点的用户数达到自己设置的数量后就释放等待,并发运行后面的脚本。
可以在多个脚本上设置相同的集合点名称来实现多个脚本同时并发的效果。
1.集合点超时
在脚本运行时,每个虚拟用户到达集合点时都会去检查一下集合点的策略设置,如果不满足,那么就在集合状态等待,直到集合点策略满足后,才运行下一步操作。但是可能存在前一个虚拟用户和后一个虚拟用户达到集合点的时间间隔非常长的情况,所以需要指定一个超时的时间,如果超过这个时间就不等待迟到的虚拟用户。
超时时间是指虚拟用户之间的时间差,当出现两个虚拟用户到达集合点的时间差超过设定的超时时间时,所有在集合点处于等待状态中的用户将全部释放。
2.集合点和事务
集合点应该放在事务外,如果事务内存在集合点,那么虚拟用户在集合点等待的过程也会被算入事务时间,导致早进入集合点的用户的响应时间有误。
常见的田径比赛就是这样,大家先集合在同一起跑线上,鸣枪后开始计时,达到终点再计时,这样就能得到准确的事务时间。
A.11 小结
VuGen是性能测试开始的第一步,也是性能测试最重要的一个步骤,如何形成一个有效的负载脚本,决定了性能测试的实施分析能否在一个坚强可靠的基础上进行。本章详细介绍了VuGen的使用方式和脚本开发的各个要点。
脚本开发是一个体力活,通过自行编写一个脚本对服务器进行负载,而脚本开发又基于需求,在掌握用户真正的行为特征后,即可根据其开发性能测试脚本。另外一个方面,根据性能测试的目的,脚本可以分为两大类:一类是针对某个具体功能的调试型性能测试脚本,这种脚本的操作尽可能少,主要是为了测试某一个功能而开发的;另一种是模拟用户行为的脚本,该脚本完全模拟了用户的常见操作及逻辑分支,主要是为了进行稳定性测试或系统级别的性能测试而开发的。
在完成了脚本的开发后,接着可以开始设计场景完成性能测试负载工作了。在下一章将会介绍如何使用Controller进行性能测试场景设计、负载生成及数据监控。
本章需要掌握的重点:
VuGen录制脚本的录制等级和设置方式
VuGen脚本录制的流程及生成脚本的原理
如何设置脚本的Runtime Settings,特别是Run Logic(运行逻辑)
参数化原理及各种参数取值的方式
关联原理及如何准确获得服务器返回的部分内容
事务时间和响应时间的组成
手工事务的使用方法
集合点策略
LR11新版本技术掌握的重点:
XML参数的处理技术
使用正则表达式处理关联
DFE技术的原理及使用
版权声明:51Testing软件测试网获作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。
相关文章: