USKSA Performance Test Script

截止12/04/2015 对USKSA (https://usksa.nextxnow.com/)做了Web并发压力测试 ,流程包括

  1. User Login(demo.web)
  2. Select Restaurant——>Input "0000034"Keyword, and put search button
  3. Fill In Section:Input  RGM Name/Input  Mic Name/choose  MIC Position/choose  Visit Type(Test)/ choose DayPart
  4. Continue Button ——>Create Performance
  5. Change to “Compliance Check” Tab ——>EvalItem DEV0000081 To NO and choose item  input comment
  6. Eval Perform Item  (Exterior/SpeedService/FOH/BOH&ALL Equipment /Product/People/Hazard/Document) All to yes tab by tab
  7. Change to Key Findings for Action
  8. Non-Scored Questions
  9. Change to "Summary"Tab  and Input General Comment
  10. Push Button“Ready to Print”  locked and upload perform

其中经验总结有几点:

1. LoadRunner11(**版)录制web对浏览器的限制

一般选择WEB(HTTP/HTML)协议方式录制(此方式跟AJAX协议相互包含),但是录制过程中事件(event)一直为0,后来经过排查和查阅资料发现Loadrunner11不支持IE11的录制,只支持到IE9 或者FireFox24.0版本。

2.脚本回放时候的error:

Error -27778: SSL protocol error when attempting to connect with host "…" 

出现此现象原因可能

1.不同脚本拷贝到不同的机器上发送请求,服务器可能认为会有安全风险,

2.多次执行脚本时,网速不佳状况下有时会出现此问题。(但有时又是好的)

解决方案(一)

在virtual user generator 内,vuser->run-time  settings->preferences->advanced里勾上WININET replay instead of Sockets。

解决方案(二)

在WEB_URL请求前加

web_set_sockets_option("SSL_VERSION", "TLS");

TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。

与SSL的区别可以参考相关资料。

3.打印请求的request、response值,好便于观察调试脚本,可以用到如下函数:

web_save_header( constchar *type, const char *param )

constchar *type,为REQUEST和  RESPONSE

比如:在update 过程中,需要打印update的请求和返回值

 

脚本中代码段:

	web_save_header(REQUEST,
		"update_RequestsString");
	web_save_header(RESPONSE,
		"update_ResponseString");


   web_custom_request("update_81",
		"URL=https://usksa.nextxnow.com/controller/eval/update",
		"Method=POST",
		"Resource=0",
		"RecContentType=application/json",
		"Referer=https://usksa.nextxnow.com/web/index.html",
		"Snapshot=t19.inf",
		"Mode=HTTP",
		"EncType=application/json; charset=UTF-8",
		"Body={\"evalItem\":{\"performId\":\"{PerformId}\",\"versionId\":\"USK.MST.2015.11\",\"itemId\":\"DEV0000081\",\"parentId\":\"FU00000015\",\"dataType\":\"Standard\",\"dataLevel\":\"3\",\"answerType\":\"YES_NO\",\"pointValue\":1,\"deductPoint\":0,\"earnPoint\":0,\"priority\":0,\"optional\":false,\"ancestorIds\":\"\\\"SC00000003\\\",\\\"FU00000015\\\"\",\"value0\":\"N\",\"value1\":\"\",\"value2\":\"\",\"value3\":\"\",\"value4\":1,\"value5\":\"\",\"value6\":\"\",\"value7\":\"\",\"value8\":\"\",\"value9\":\"\",\"value10\":\"\",\"bizLevel\":1,\"bizLevelUpgraded\":1,\"bizLevelMax\":2,\"rptPlatform\":\"\",\"bmu\":\"\",\"concept\":\"\",\"validationType\":\"\",\"validationRule\":\"\",\"valueList\":\"\",\"videoLink\":\"\",\"fsc\":1,\"fsiCode\":\"CL101\",\"displayId\":\"\",\"photo\":false,\"maint\":false,\"detailRequired\":false,\"qualityGuide\":\"RFIL000163\",\"summaryCat\":\"SMCT000003\",\"desc0\":\"Exterior building and all exterior structures are clean.\",\"desc1\":\"\\u25cf No soil on exterior building or other str"
		"uctures (such as fences, retaining walls and decking), windows (sills, frames, seals), doors (frames, handles, thresholds, locks/keypads, closures, hardware), lights (fixtures, functioning), roof (tile, gutters, drains/downspouts), awnings or any element of the building.  \\n"
		"\\u25cf There should be no dirt buildup, cobwebs, graffiti, mildew, algae, smear marks, streaks or fingerprints.  \\n"
		"\\u25cf Guest Touch Point Job Aid must be available to team members.\\n"
		"\\n"
		"WHY is this important?\\n"
		"\\u2022 Poor appearance and condition of the restaurant exterior is shown to have a significant impact on a Guest's decision on whether or not to visit a restaurant.\\n"
		"\\u2022 The best way to invite Guests into the restaurant is to impress them with a clean and well-maintained exterior.\\n"
		"\",\"desc2\":\"\\u25cf One of anything listed (webs, grafitti, buildup dirt, etc.) is an issue.\\n"
		"\\u25cf Do not deduct if missing Guest Touch Point Job Aid is the only issue.\",\"desc3\":\"Allow 2 issues.\\n"
		"L1 if 3-4 issues.\\n"
		"L2 if >4 issues.  \",\"selectionList\":null,\"reoccurring\":0,\"occurredInMR\":false,\"qualityGuides\":[{\"itemId\":\"RFIL000163\",\"desc0\":\"Guest Touch Points job aid\",\"desc1\":\"/ROCC.FILE.2014.01/RFIL000163/RFIL000163_en_US.pdf\",\"displayOrder\":null,\"detailRequired\":false,\"rawValue\":null,\"label\":null,\"path\":null,\"uri\":null}]},\"operationType\":\"answer\"}",
		LAST);

	lr_output_message("-------update_RequestString result----------- : %s", lr_eval_string("{update_RequestsString}"));
	lr_output_message("-------update_ResponseString result----------- : %s", lr_eval_string("{update_ResponseString}"));

4 从Response中获取performId。

web_reg_save_param() 此方法是注册函数:(需要写在请求之前,则从服务器返回的response字符串中根据左右边界查找字符串)

is a registration type function. It registers a request to find and save a text string within the server response. The operation is performed only after executing the next action function, such as web_url.

脚本中的代码段:

 web_reg_save_param("PerformId",
		"LB=\"performId\":\"",
		"RB=\",",
		"Search=Body",
		LAST);
	web_custom_request("create",
		"URL=https://usksa.nextxnow.com/controller/eval/create",
		"Method=POST",
		"Resource=0",
		"RecContentType=application/json",
		"Referer=https://usksa.nextxnow.com/web/index.html",
		"Snapshot=t10.inf",
		"Mode=HTTP",
		"EncType=application/json; charset=UTF-8",
		"Body={\"storeInfo\":{\"bmu\":\"USK\",\"concept\":\"Kx\",\"account\":\"\",\"brand\":\"KFC\",\"accountNumber\":\"0000034\",\"ownerFirst\":null,\"ownerLast\":null,\"address\":\"2971 Moose Trail\",\"city\":\"Elkhart\",\"state\":\"IN\",\"zip\":\"46514\",\"phone\":\"5749701522\",\"type\":\"License\",\"status\":\"O\",\"acFirst\":null,\"acLast\":null,\"morFirst\":null,\"morLast\":null,\"specNtId\":null,\"cntryCode\":null,\"closeDate\":null,\"reopenDate\":null,\"importStatus\":null,\"storeName\":null,\"accountName\":\"\",\"owner\":\"Paul Kelly\",\"keyOp\":\"\",\"ac\":\"\",\"country\":null,\"fax\":null,\"x1key1first\":null,\"x2key1last\":null,\"acEmail\":null,\"morEmail\":null,\"ownerEmail\":null,\"regnEmail\":null,\"fbcEmail\":null,\"rgmEmail\":null,\"generalManage\":null},\"selectedConcept\":\"Kx\",\"mic\":\"AnnieTestUpdate\",\"micPosition\":\"RGM\",\"userId\":\"demo.web\",\"userSeqId\":1328,\"firstName\":\"Demo\",\"lastName\":\"Web\",\"visitType\":\"Test\",\"beginTime\":\"9:37 AM\",\"endTime\":\"\",\"dayPart\":\"Lunch 11-"
		"2\",\"rating\":\"\",\"ratingValue\":\"\",\"lockDate\":\"\",\"languageCode\":\"en_US\",\"rgmName\":\"urlAnnie\",\"previousPlan\":\"\",\"generalComments\":\"\",\"performType\":\"CER\"}",
		LAST);
    
	lr_end_transaction("CreatePerform", LR_AUTO);

    lr_output_message("-------PerformId is----------- : %s", lr_eval_string("{PerformId}"));

具体方法用法请查看官方API

5.Cookies的获取(虽然最终cookies没有用到,此项目请求中是自带的)

用C语言中sscanf()方法在response字符串中筛选  用法可以参考http://www.2cto.com/kf/201304/206588.html

在脚本中用sscanf()方法用来获取cookies,虽然最后没有派上用场,但是日后可以作为经验来参考cookies的获取 脚本中代码段如下:

//sscanf(lr_eval_string("{requesHeader}"),"POST /j_spring_security_check HTTP/1.1'\n'Cookie: JSESSIONID=%'\n'Content-Type:", s1);
	sscanf( lr_eval_string("{requesHeader}"),
		"%s %s %s %s %s",
		s1,
		s2,
		s3,
		s4,
		s5 );
	//运用sscanf() 函数,通过截取来获取字符串
//lr_output_message("s1=%s", s1);
//lr_output_message("s2=%s", s2);
//lr_output_message("s3=%s", s3);
//lr_output_message("s4=%s", s4);
	lr_output_message("s5---%s", s5);
	sscanf( s5,	
			"%[^=]=%[^=]",
				cookieString,
			  sessionId );
	//在“=”号之间将字符串拆成两段cookieString and sessionId
	//lr_output_message("cookieString----%s", cookieString);
	lr_output_message("sessionId----%s", sessionId);

 6.判断执行失败

方案一:推荐

可以用注册函数查找success:false (注意,有时一个请求的返回值有多个success的字段,注意分析,通过限定左右边界找出正确指引执行成功或失败的json字段),然后通过C语言中strcmp("str1","str2")==0来判断两个字符串是否相等。

if(strcmp(lr_eval_string("{Params_CreateSuccess}"),"false")==0 ) {
	lr_error_message("CreatePerform Failed");
	return -1;
}
lr_start_transaction("CreatePerform");


//Each create/update can use the follow source to check if it's success
	web_reg_save_param("Params_CreateSuccess",
		"LB=\"success\":",
		"RB=,\"code\":",
		"Search=Body",
		LAST);
//If web_reg_save_param failed(not found "success":), this transation will failed
//If web_reg_save_param success, check the params to see if it's true

	
	web_custom_request("create",
		"URL=https://usksa.nextxnow.com/controller/eval/create",
		"Method=POST",
		"Resource=0",
		"RecContentType=application/json",
		"Referer=https://usksa.nextxnow.com/web/index.html",
		"Snapshot=t10.inf",
		"Mode=HTTP",
		"EncType=application/json; charset=UTF-8",
		"Body={\"storeInfo\":{\"bmu\":\"USK\",\"concept\":\"Kx\",\"account\":\"\",\"brand\":\"KFC\",\"accountNumber\":\"0000034\",\"ownerFirst\":null,\"ownerLast\":null,\"address\":\"2971 Moose Trail\",\"city\":\"Elkhart\",\"state\":\"IN\",\"zip\":\"46514\",\"phone\":\"5749701522\",\"type\":\"License\",\"status\":\"O\",\"acFirst\":null,\"acLast\":null,\"morFirst\":null,\"morLast\":null,\"specNtId\":null,\"cntryCode\":null,\"closeDate\":null,\"reopenDate\":null,\"importStatus\":null,\"storeName\":null,\"accountName\":\"\",\"owner\":\"Paul Kelly\",\"keyOp\":\"\",\"ac\":\"\",\"country\":null,\"fax\":null,\"x1key1first\":null,\"x2key1last\":null,\"acEmail\":null,\"morEmail\":null,\"ownerEmail\":null,\"regnEmail\":null,\"fbcEmail\":null,\"rgmEmail\":null,\"generalManage\":null},\"selectedConcept\":\"Kx\",\"mic\":\"{mic}\",\"micPosition\":\"RGM\",\"userId\":\"demo.web\",\"userSeqId\":1328,\"firstName\":\"Demo\",\"lastName\":\"Web\",\"visitType\":\"Test\",\"beginTime\":\"9:37 AM\",\"endTime\":\"\",\"dayPart\":\"Lunch 11-"
		"2\",\"rating\":\"\",\"ratingValue\":\"\",\"lockDate\":\"\",\"languageCode\":\"en_US\",\"rgmName\":\"urlAnnie\",\"previousPlan\":\"\",\"generalComments\":\"\",\"performType\":\"CER\"}",
		LAST);
lr_output_message("%s",lr_eval_string("{Params_CreateSuccess}"));

if(strcmp(lr_eval_string("{Params_CreateSuccess}"),"false")==0 ) {
	lr_error_message("CreatePerform Failed");
	return -1;
}

	
	lr_end_transaction("CreatePerform", LR_AUTO);

 方案二:web_reg_find()通过此函数,(需要写在请求之前)来查找指定字符串,以及出现的次数 比如

   web_reg_find("Text=HTTP/1.1 200 OK",
         "SaveCount=userAccess_Count",
         LAST );

   web_reg_save_param("userAccessResponse_success",
	 "LB=",
     "RB=Server",
	LAST);
   web_custom_request()...

 方案三:通过web_custom_request的返回值 来判断执行是否成功(成功返回0,失败返回-1?)

int status_login ;	
status_login =web_custom_request("userAccess", 
		"URL=https://usksa.nextxnow.com/controller/security/userAccess", 
		"Method=POST", 
		"Resource=0", 
		"RecContentType=application/json", 
		"Referer=https://usksa.nextxnow.com/web/index.html", 
		"Snapshot=t3.inf", 
		"Mode=HTTP", 
		"EncType=", 
		LAST);
	

 
 lr_output_message("status_login is %d",status_login);

 方案四: 通过请求后的返回HTTP状态值判断200是否成功(慎用),需要写在请求之后。

web_get_int_property(HTTP_INFO_RETURN_CODE);

int HttpRetCode_Create;
HttpRetCode_Create = web_get_int_property(HTTP_INFO_RETURN_CODE); 
 lr_output_message("HttpRetCode_Create is %d",HttpRetCode_Create);
if (HttpRetCode_Create!= 200) 
{lr_error_message("CreatePerform Failed");
CreatePerform();

}

 方案五:  通过判断标记的事物状态。

lr_get_transaction_status("LockPerform") == LR_FAIL

例如LockPerform

LockPerform()
{
	 lr_start_transaction("LockPerform");

	web_submit_data("lockPerform",
		"Action=https://usksa.nextxnow.com/controller/eval/lockPerform",
		"Method=POST",
		"RecContentType=application/json",
		"Referer=https://usksa.nextxnow.com/web/index.html",
		"Snapshot=t38.inf",
		"Mode=HTTP",
		ITEMDATA,
		"Name=performId", "Value={PerformId}", ENDITEM,
		LAST);
lr_end_transaction("LockPerform", LR_AUTO);
//--------------------------------------------------------failed to lock------------------------------
 
if (lr_get_transaction_status("LockPerform") == LR_FAIL)
    {
        lr_end_transaction("LockPerform",LR_FAIL);
		lr_error_message("Error:  Unable to LockPerform");
         return -1;
    }
 //--------------------------------------------------------failed to lock------------------------------

	

	return 0;
}

7. 脚本中Action()的模块化

脚本中包括三大块vuser_init \ vuser_action\vuser_end

vuser_init是初始化模块可以将登陆等初始化内容放在此处。效果则是:

每5S登陆一个用户,直到500个登录完毕才会一起进行Action。(此项目中测试场景不需要登录后 等待进行并发,所以不必将login写在初始化模块)

vuser_end 是结束模块,可以将Logout等脚本放在此模块(此项目中如果要求LockPerform,可以将LockPerform放在此处)。

 vuser_action是真正执行业务的模块,我们其实可以新建很多Action,用模块化的思想把业务分开,以便在回放的时候好调用和控制。如图:

USKSA Performance Test ScriptUSKSA Performance Test Script

8、场景的设计

一开始在并发场景设计中沿用了以前的经验,除去初始化和结束的Vuser,中间采用运行30分钟的方法进行300并发。结束后发现数据库中的记录条数(create perform数量)与Vuser的数量对应不上。(按照常理应该每个Vuser 在结束后只应该创建一个perform,但是数据库里的数量多了很多倍)检查了脚本等地方没有发现问题,一度比较沮丧。第二天一早发现问题所在:整个录制的业务流程一直跑到最后是locked的状态,并发的时候其实脚本跑的很快,有的Vuser几十秒就能锁定一个perform,而有的可能会几分钟还在跑。所以硬性规定场景要运行的时间,(比如30min)势必会造成一种局面:结束的Vuser locked了perform ,但是为了要跑够30min所以继续create perform来重新执行N遍脚本,直到30min结束。所以创建了很多冗余的数据。

USKSA Performance Test Script

分析后场景更改为:Duration choose  “Run until completion” 意思为“场景运行直到脚本结束”,数据库生成的perform条数才是正确的。所以要不同的项目分析不同的业务性质才来设置运行场景,不可生搬硬套。

USKSA Performance Test Script

 

在美国要求的测试场景:

Test Parameters:根据要求设计压力场景
Total Concurrency: 500
Ramp up time: 1 every 5 seconds
Total max concurrency time 15 minute
Ramp down time: 1 every 5 seconds

这种要求下应该去掉LockPerform的Action()防止冗余数据的发生。

 

6.分布负载客户机器来并发脚本(并发人数要求很多的情况)

如果并发人数大于200以上,需要考虑分布不同客户机来负载发送服务器请求,虽然公司的PC机性能很好,一台小型工作站负载500并发完全可以,但是网络带宽可能支持不住,导致并发结果大打折扣。

此时则最好采用负载客户机(一般200Vuser/PC)分布

 

详细请参考 ftp://ftp.easternphoenix.com/staffs/annie.wang/Test Script/USKSA_LoadRunner11**版       文件夹下的report和脚本

脚本的版本包括:

Annie1130_V6.0(模块化后)(前后分别并发的100/200/500 多次并发报告以及过程 USKSA_PerformaceTestReport.doc

以及调试之后的美国脚本: USKSA_Script1204(对应生成的usksa stress test.pdf 压力测试报告)

此处不再赘述。