性能测试是一项浩大的工程,若你只想随便找台机器装上ld后,造几条数据,弄几个并发用户简单跑一下出来结果就可以万事大吉了,那你就大错特错了!(这样得出的测试结果没有任何价值和意义,当然更无法依此评估出你贵公司系统的性能了。
性能测试真正开始执行之前除了编写详细的性能测试计划【所需的资源(软件+硬件+人力)】、设计测试脚本、准备测试数据、搭建测试环境外,还需要注意一下细节:
如何保证性能测试的顺利开展和执行?
首先考虑你性能测试的目标是什么,需要哪些人员协助你才能完成,然后协调相关人员(DBA、网管、开发人员等),保证在真正开展过程中能有效得到他们的协助和支持(性能测试不是一个人就能完成的,除非你“全才”啦);
你计划中需要申请的资源,比如运行contoller的机器,是否符合你的预期要求,Cpu是否有足够的处理能力,安装的操作系统是否符合你的要求(loadrunner9.5除load Generator外都不能安装在64位机操作系统下,若没看清楚安装文件(安装程序下help/install.pdf)中system requirements for installing说明的话,你安装完成会发现自己白忙活了,还得重装OS,然后重来一次);
你要测试的程序是否功能都没问题了,若程序还有变更,请千万不要在录制部分后又变更了,你需要的版本是一个功能稳定的版本,能顺利录制脚本的的版本);
-
在测试执行前你是否召集开发和相关人员对程序中明显需要优化的地方(你功能测试执行时系统有些功能就无法忍受的慢)进行了优化,这样可以大大缩短你的性能测试周期;
在选择loadrunner工具前,一定要慎重,你的程序设计语言和架构及其所运用的技术,此工具是否都支持,不然后续你需要自行开发的脚本就太多了,可能面临重新选择测试工具的严重问题);
分险分析:技术风险、风险分析、分险应对措施和风险监控方法。
设计测试脚本?
- 识别可能的系统性能问题,多与相关人员分析讨论。
- 你所测系统的重点业务是什么?都有哪些角色参与?业务逻辑是什么样的?用户频繁使用的功能是否都考虑周全了?
- 参数化数据的来源?都需要哪些检查点?脚本的精简程度?
准备测试数据?
- 基础数据:要更符合实际需求,人员、角色、初始化数据等;
- 业务数据;要更符合实际业务,数据最好不要相同的数据,无效的数据,要类别丰富、覆盖所有业务逻辑的基础数据;可以通过自动化工具直接生成;数据库脚本生成(单一数据,关联几个表的数据最好不用脚本生成);用ld生成。
搭建测试环境?
- 网络(带宽、可使用的有效ip地址个数);
- 服务器的配置;
- 当前测试环境的局限性(无法模拟的测试环境都有哪些)。
需求分析和需求转化
客户的性能需求不可测试、没有需求、需求模糊,要通过与客户、开发人员的沟通获得可测试、可衡量和可量化的性能需求
1.8/2原则
2.经验值
3.平均并发用户数C=nL/T(n:用户数量[login session的数量],L:用户平均使用时长[login session的时长],T:考察的时间段)
4.并发用户峰值:C1=C+3√C
1、从hp官方网站上下载loadrunner9.5的试用版程序(有windows和linux的版本);
2、按照步骤进行安装(安装之前先安装必须的.net框架和其他组件);
3、安装文件的目录及文件存放路径一定不能含有中文,防止错误;另外最好不要选择根目录下,要新建一个文件夹,命名,然后安装其下,安装完成后,重启。
4、从http://support.openview.hp.com/selfsolve/document/KM750376下载LoadRunner9.51 patch安装LoadRunner_951.msp文件
然后http://support.openview.hp.com/selfsolve/document/KM793318下载LoadRunner 9.52 patch安装LoadRunner_952.exe文件
Ld9.50对flex的支持有以下问题:
l 无法解析flex内部的对象;
l 在一台机器上录制的脚本无法在另一台机器上正确运行;报错如:
java.io.IOException: file c:/jars/syswincrm3.jar is not found
at hp.flex.ClassLoaderManipulator.loadClassPathString(ClassLoaderManipulator.java:58)
(这是在调试日志中显示的/HP_FLEX_JAVA_LOG_FILE/,下面是在界面中的Replay log中显示的)
Error: Encoding of AMF message failed. Error is : Exception Occurred while invoking WriteObject method - java.lang.ClassNotFoundException: flex.messaging.io.amf.Amf3Output
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:164)
at hp.flex.HPExternalizableWrapper.GetLCDSObjectOutputStream(HPExternalizableWrapper.java:155)
at hp.flex.HPExternalizableWrapper.GetObjectOutputStream(HPExternalizableWrapper.java:215)
at hp.flex.HPExternalizableWrapper.WriteObject(HPExternalizableWrapper.java:23)
l 在Controller中添加其他ip地址(已经安装了agent process)Load Generators,运行报错;
l Results Analysis中的Set Global Filter中没有thinktime选项,这样就不能查看不包含thinktime的事务响应时间了;
l 其他问题可在安装后的readme.htm中查看
升级到Ld9.52后则解决了此问题,这两个补丁所解决的问题可以在下载的压缩包中解压的readme.htm文件中查看。
5、然后用传统的破解方法进行破解即可。google,破解方法如下(仅供学习研究人员参考,请大家支持正版软件!):
l 用LR8.0中的mlr5lprg.dll、lm70.dll覆盖LR9.5安装目录下“bin”文件夹中的对应文件;
l 手动修改注册表,删除下面内容:
[HKEY_LOCAL_MACHINE/SOFTWARE/Mercury Interactive/LoadRunner/License2]
[HKEY_LOCAL_MACHINE/SOFTWARE/MercuryInteractive/LoadRunner/License2/History]"AIBGEBFW-JVED-ZKEKEKEKEKEBDNQAF-KBRDN"="" [HKEY_LOCAL_MACHINE/SOFTWARE/MercuryInteractive/LoadRunner/License2/PermanentLicense] @="AIBGEBFW-JVED-ZKEKEKEKEKEBDNQAF-KBRDN"
"last"="AIBGEBFW-JVED-ZKEKEKEKEKEBDNQAF-KBRDN" [HKEY_LOCAL_MACHINE/SOFTWARE/MercuryInteractive/LoadRunner/License2/TemporaryLicense]@="AEBGEBFS-AKEKEKEKE-KAUCA" [HKEY_LOCAL_MACHINE/SOFTWARE/Classes/Interface/{87B3ADD4-21EB-11d5-93EF-00105AA0FD2D}] @="IControl"
//当然也可以直接执行删除注册的文件lr_Del_license(regedit).exe。
l 然后使用老的注册码就可以使用了;
global-100: AEAMAUIK-YAFEKEKJJKEEA-BCJGI
web-10000: AEABEXFR-YTIEKEKJJMFKEKEKWBRAUNQJU-KBYGB
or(Ok――1000个用户的Global License:AEACFSJI-YASEKJJKEAHJD-BCLBR)
6、安装后的配置:
l 在用户环境变量中增加HP_FLEX_JAVA_LOG_FILE的debug参数 ;value为:目录/日志文件名称。
Eg: C:/ldlog/flex.log
l 在录制脚本的Vugen中的Toole->Recording Options->Flex ->Externalizable Objects下添加Lib文件,这样就可以正常解析测试系统中的定义的类和对象了,并且使得脚本中的文件结构更加清晰。
添加的文件:
flex-messaging-common.jar
flex-messaging-core.jar
其他与flex和spring相关的文件;
测试系统自定义的jar包,如:syswincrm31.jar
这样就可以正常解析flex协议下所有请求数据了,并且可以使得录制的文件的结构更加清晰,方便参数化。
1、 录制时无法弹出IE,google一下,解决办法如下:
l 主要是LR的注册信息被修改,无法找到IE路径。如何重新注册LR呢?在lr的安装目录(例如D:/Program Files/Mercury/LoadRunner/bin)下,单击register_vugen.bat文件,注册信息被重新改写了。不过别忘了最后一步。重新启动一下,好了;
l 有多个浏览器时,将IE置为默认浏览器;由于IE的第三方插件的影响,IE工具-》Internet选项…-》高级,把“启用第三方浏览器扩展”的选中去掉即可;
l 对于Windows2003上无法录制IE6/7,导致IE Crash,iedw.exe(IE Crash Detection)报告错误事件,可能原因是Windows Server默认对IE加载DEP(数据执行保护)特性,而Vugen.exe刚好又跟DEP冲突。方法:我的电脑>右键属性->高级->“性能”部分的“设置”->“数据执行保护”->点击“添加”按钮->将LR安装目录bin下的vugen.exe添加进去->点击确定,重启一下LR,就可以录制了。
2、 录制不到脚本
l 选择的协议不对,有时需要选择多个协议,这个需与开发、系统架构师沟通;
l 没有选择jar包;
l 检查防火墙个杀毒软件,关闭所有的网络监测;
l 早期的版本(Ld8.0以前的版本)不能很好的支持IE6以上的版本,换成IE6即可;
l Flex需要客户端下安装flash,并且IE选项的安全设置中设置可以访问所有active控件;
3、 录制的脚本回放错误
l 选择的协议不对(应选择多个协议或其他协议);
l 没有选择jar包,无法解析数据;
l 自动关联错误;
l 动态数据没有做关联;
Flex协议录制的脚本没有办法产生自动关联时,需要下面的Ld中自带的函数进行自定义:
l 选择另一台机器上录制的ld9.50版本的脚本,原来选择的http/flex两个协议的脚本,回放报错Replay log如下:
Error: Encoding of AMF message failed. Error is : Exception Occurred while invoking WriteObject method
- java.lang.ClassNotFoundException: flex.messaging.io.amf.Amf3Output
(Enter to WriteObject WriteObject throws exception:
java.lang.ClassNotFoundException: flex.messaging.io.amf.Amf3Output――后台debug log)
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:164)
at hp.flex.HPExternalizableWrapper.GetLCDSObjectOutputStream(HPExternalizableWrapper.java:155)
at hp.flex.HPExternalizableWrapper.GetObjectOutputStream(HPExternalizableWrapper.java:215)
at hp.flex.HPExternalizableWrapper.WriteObject(HPExternalizableWrapper.java:23)
无法解析,原来机器上的jar包的位置在本机器上不同引起的
因缺少录制时候选择的jar包,选择的jar包的位置变化了或者引用的jar包已经删除了。
解决方法:重新在Run-time settings->Flex->Externalizable Objects中增加jar包(因为原来引用的jar包的位置已经发生变化了),增加Recording Options中的jar是没用的。
l 重复记录导致:Error: Server returned error for message #1 : "AMF call returned an error, described in XML seen in extended log"可以用打开所有log(Run-time Setting->General->Log->Extended log,全部选择上),然后回放脚本,进行具体问题的跟踪,一般是由于程序中限定不允许数据重复(或数据记录的某个字段不能重复导致的,一般发生在新建和修改里面。(有些可能不是程序中作限定,而是数据库中有唯一性限制,索引或主健等),删除相关的所有记录后再次回放即可。(本系统中的此次测试出现这个错误是由于删除了客户台帐的新建客户数据,但在客户会中生成的主数据没有删除导致的)
l 版本不兼容,在一个ld版本下录制的脚本在一个版本下使用。
l 请求超时或修改了程序(但你不知道)。
l 根据Replay log的具体错误内容查看原因(结合Replay log(F10单步执行)、web服务的后台日志和程序综合分析)注:web服务后台日志的显示级别在/WEB-INF/classes/log4j.properties或者中application-context.xml中的props设置(最好只显示错误,不然日志文件太大,不容易分析)。
4、 录制的可以回放的脚本却不能操作成功
l 对动态数据没有做关联;
l 有验证码:如果请求进去后,服务器虽然判别请求非法,但是它仍能成功处理,告诉你请求数据有问题,那么它返回的http代码仍是200!只是页面上会有它的提示信息错误!LR只判断请求的状态是否正确,不判断业务数据是否正确!如果要做判断得自己写检查点判别!
l web服务器后台日志显示Unknown AMF question的错误,请求无效导致
l 选择的协议不完整,录制的脚本不完整导致的;
l 打开Extended Log单步执行查看Replay log和web服务后台日志,然后分析
1、 脚本参数化之前所做的工作:
l 保存录制好的一个原始的脚本;然后另存一份进行参数化(备份一份,以防参数化错误导致无法恢复初始脚本而重新录制脚本,浪费时间);
l 然后把需要关联的进行关联,然后回放成功且测试系统中成功(eg:能看到新增的相应记录)再保存一份;
l 脚本中是否有注释,没有则添加注释,增加脚本可读性,方便参数化和以后重用;
l 脚本中是否建立了事务的开始和结束点(录制时需要考虑的);
l Session和动态数据是否正确和全部进行了参数化替换,(注意一个参数录制后分行的情况,要截取一部分通过查找功能一一检查脚本;参数前面的特殊字符不进行替换)eg如下:
flex_web_request("amf;jsessionid="
"LFgVY20Tly1K0pQqprsXvwFpKhvqg5KznhJn43syvgXGGpyDNh42!1755959435",
"URL=http://erp.syswin.com/messagebroker/amf;jsessionid="
"LFgVY20Tly1K0pQqprsXvwFpKhvqg5KznhJn43syvgXGGpyDNh42!1755959435",
"Method=POST",
"Resource=0",
"RecContentType=application/x-amf",
"Referer=http://erp.syswin.com/syswincrm3.swf",
"Snapshot=t3.inf",
"Mode=HTML",
"EncType=application/x-amf",
"BodyBinary=//x00//x03//x00//x00//x00//x01//x00//x04null//x00//x02/"
"1//x00//x00//x01//x18/n//x00//x00//x00//x01//x11/n"
"//x81//x13Oflex.messaging.messages.RemotingMessage//x13operation/r"
"source/t"
"body//x13messageId//x11clientId//x15timeToLive//x13timestamp//x0Fheade"
"rs//x17destination//x06//x0Blogin//x01/t//x05//x01//x06//x07gwa//x06/r"
"666666//x06IAACD40DF-FFC4-8641-4CB8-02F1481FB0F4//x01//x04//x00//x04//"
"x00/n//x0B//x01//x15DSEndpoint//x06/rmy-amf/t"
"DSId//x06I3E3B399F-351B-D1FC-FC78-81B50BD94526//x01//x06//x1DsessionSe"
"rvice",
LAST);
flex_amf_call("AMF3_call_1",
"Gateway=http://erp.syswin.com/messagebroker/amf;jsessionid="
"LFgVY20Tly1K0pQqprsXvwFpKhvqg5KznhJn43syvgXGGpyDNh42!1755959435",
"Snapshot=t4.inf",
MESSAGE,
"Method=null",
"TargetObjectId=/2",
BEGIN_ARGUMENTS,
"<AMF3><object-externalizable-custom>"
"<flex.messaging.messages.RemotingMessage>/n <destination"
">sessionService</destination>/n <messageId"
">4ADBA44D-56DE-48EF-3CF0-02F14929E669</messageId>/n <timestamp>0</"
"timestamp>/n <timeToLive>0</timeToLive>/n <headers>/n <entry>/n "
" <string>DSEndpoint</string>/n <string>my-amf</string>/n </"
"entry>/n <entry>/n <string>DSId</string>/n <string"
">3E3B399F-351B-D1FC-FC78-81B50BD94526</string>/n </entry>/n </"
"headers>/n <operation>getLoginInfo</operation>/n <parameters/>/n</"
"flex.messaging.messages.RemotingMessage></object-externalizable-custom"
"></AMF3>",
END_ARGUMENTS,
LAST);
上面录制的脚本通过回放对比(脚本的View Tree模式下的Server Response可以很明显的看到jsessionid需要关联,直接关联参数化替换即可;而DSid也需要关联,然而通过对比脚本的上下文发现前面的x06I并不需要参数化(后面的此相关内容中都没有x06I)
l 保存脚本后,在每个脚本中增加readme文件,简要描述一下脚本的录制内容、多少个参数及其参数运行机制等。
l 以上的文件保存还有参数化及其脚本中的注释一律用英文(Ld对中文支持的不好)
2、 Flex中sessionid如何关联?
l 首先在Generation Log中查找第一返回sessionid的AMF Response的xml文件(也可以在View Tree模式下查看第一个flex amf call 后的Server response中返回的内容,然后右侧选择Snapshot (Both+XML)得到如下xml文件,但目前的版本中没有看到此文件)eg:
AMF notify: AMF Response xml
<AMFPacket AMF_version="3">
<AMFHeaders>
<AMFHeader name="AppendToGatewayUrl" must_understand="true">
<string>;jsessionid=LFgVY20Tly1K0pQqprsXvwFpKhvqg5KznhJn43syvgXGGpyDNh42!1755959435</string>
</AMFHeader>
</AMFHeaders>
<Messages>
<Message method="/1/onResult" target="">
<AMF3>
<object-externalizable-custom>
<flex.messaging.messages.AcknowledgeMessageExt>
<byte>-88</byte>
<byte>3</byte>
<flex.messaging.io.amf.ASObject serialization="custom">
<unserializable-parents/>
<map>
<default>
<loadFactor>0.75</loadFactor>
<threshold>12</threshold>
</default>
<int>16</int>
<int>2</int>
<string>DSId</string>
<string>3E3B399F-351B-D1FC-FC78-81B50BD94526</string>
<string>DSMessagingVersion</string>
<double>1.0</double>
</map>
<flex.messaging.io.amf.ASObject>
<default>
<inHashCode>false</inHashCode>
<inToString>false</inToString>
</default>
</flex.messaging.io.amf.ASObject>
</flex.messaging.io.amf.ASObject>
<long>1267065045765</long>
<byte-array>Pjs5nzUreVPrVzhfC43tVg==</byte-array>
<byte-array>Pjs5nzU9e4JlWT2jzHpllQ==</byte-array>
<byte>2</byte>
<byte-array>k/Z0Cs1L2rKvVALxSC+pkg==</byte-array>
<byte>0</byte>
</flex.messaging.messages.AcknowledgeMessageExt>
</object-externalizable-custom>
</AMF3>
</Message>
</Messages>
</AMFPacket>
l 关联的函数放在第一个flex_amf_call请求的后面,在第一个出现session id的请求前面(即服务器返回的带有session的响应数据是在第一个请求后返回到客户端的)
lr_message("resp %s", lr_eval_string( "{resp}"));
lr_xml_get_values("XML={resp}", "ValueParam=seid","Query=/AMFPacket/AMFHeaders/AMFHeader/string",LAST);
lr_xml_get_values("XML={resp}", "ValueParam=dsid","Query=//string[position() mod 4 = 2]",LAST);
lr_message("seid %s", lr_eval_string( "{seid}"));
lr_save_var(lr_eval_string("{seid}") + 12, 64, 0, "sid");
lr_message("sid %s", lr_eval_string( "{sid}")); //output sid
lr_message("dsid %s", lr_eval_string( "{dsid}")); //output dsid
l 在第一个flex_amf_call请求中的MESSAGE前面增加一行
"ResponseParameter=resp", //用于获取服务器响应中的内容
3、 脚本参数化时的注意事项:
l 分析脚本,对需要参数化的内容进行参数化;
l 参数化时,对于录制的数据和参数化的数据长度不一致而脚本解析不同时,需修改参数与其一致;
Eg: 密码: 666666->脚本解析为->/r666666
1 ->脚本解析为->/x01
l 对于不需参数化的变量(项目名称或ID等),给出注释,写明变量名称,便于后续的分析和查找有无遗漏的参数;
l 对于换行的参数,替换时要完全替换,但换行符要保留;
l 参数化数据可通过连接数据库查询得到(必须安装Oracle客户端并建立本地Net服务名后方可)。Eg:
在脚本中选择要替换的参数值,选择Replace with a Parameter->properties->Data Wizard…->Specify SQLstatement manu-Next->Create->新建文件数据源->选择Oracle in OraDb10g_home1->输入一个名字(如cc)->完成->在Oracle ODBC Driver Connect窗口中Service Name中输入在客户端程序(Net Configuration Assistant中创建一个本地Net服务名配置且测试成功)中配置的名字(大写,注不用输入数据库的IP地址),然后输入登陆数据库的用户名和密码->确定->选择cc数据源->确定->再次输入服务名(Service Name)/用户名(User Name)/密码(Password)[此时只输入空白处的密码即可,其他两项已经有刚才设置的数据了]->OK ->在Connection出显示一串数据eg:DRIVER={Oracle in OraDb10g_home1}; SERVER=SYSWIN25; UID=crm39;PWD=crm39; DBQ=SYSWIN25; DBA=W;APA=T; EXC=F;XSM=Default;FEN=T;QTO=T;FRC=10;FDL=10;LOB=T;RST=T;BTD=F;BAM=IfAllSuccessful;NUM=NLS;DPM=F;MTS=T;MDI=Me;CSR=F;FWC=F;FBS=60000;TLO=O;
然后在下面SQL中输入相应的查询语句即可->Finish,在参数列表中就可以看到数据了。
l 对于参数化的数据最后是有规律的数据,这样可以通过excel表格下拉+1的方式生成;
l 对于有数据关联的情况,可以建立一个*.dat文件,然后从数据库中查询后导入,然后生成多列的参数化文件;
l 一一检查需要参数化的内容,以防遗漏,全部参数化后,回放,回放无误后,另存一份脚本。Eg: login1_OK。