背景介绍

在性能测试工作中,有时需要对业务系统所能支持的最大在线用户数目进行评估。这与我们接触最多的压力测试不一样,因为用户在线时只是与服务器保持连接,并不一定对服务器有业务请求,从而对服务器不一定会产生压力。然而,在线用户数目并非可以无限增长,当在线用户数目达到应用服务器(或者WebLogic等中间件,或者数据库连接池等)的连接数设置的极限时,业务系统同样可能会发生异常,出现新用户无法登录,或者老用户被挤出系统,甚至业务系统宕机的情况。因此,对业务系统的最大在线用户数指标进行测试是极其必要的。

现有一OA系统,需要测试其支持的最大在线用户数目。已知当使用浏览器登录该系统后,登录用户可持续地保持登录状态,即使长时间不做任何操作也不会自动退出系统;通过该OA系统的在线用户数统计模块可以详细地查看到当前在线的用户。

测试方法分析

为了测试被测系统所能支持的最大在线用户数,需要不断地使用新用户帐号进行登录操作,在此同时查看被测系统的在线用户数目以及系统的响应情况。

在新增登录用户时需要注意,由于考察的是系统在正常情况下所能支持的在线用户数目,而不是系统在并发压力下的性能响应情况,因此登录用户时最好采用单个用户或少量并发用户(如两个或三个)逐步登录的形式,不同登录批次之间最好能有一定时间间隔,务必使新增登录用户的操作对服务器产生尽可能小的业务压力。

在新增登录用户的过程中,需要对被测系统的在线用户数目进行查看,并着重关注以下几个方面:

  • 持续新增登录用户的同时,业务系统中的在线用户数目是否相应地进行增长
  • 持续新增登录用户的过程中,系统登录操作是否产生连接超时的情况,事务的响应时间是否出现大幅度上升的情况,系统登录事务是否出现失败的情况(这需要在脚本中对登录事务做检查点设置)
  • 持续新增登录用户的过程中,定期地在浏览器中手动刷新业务系统界面,查看业务系统是否出现不可访问的情况(如内部服务器错误、宕机等)

相应地,测试脚本需符合如下形式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
lr_start_transaction("进入登录页面");
/*此处为进入系统登录界面的脚本*/
lr_end_transaction("进入登录页面", LR_AUTO);

lr_start_transaction("登录系统");

/*登录脚本--部分1*/

/*对系统登录进行校验
 *若成功,即新页面中包含"重新登录",reLogin_Count>0
 *若失败,reLogin_Count=0
 */
web_reg_find("Text=重新登录",
    "SaveCount=reLogin_Count",
    LAST );

/*登录脚本--部分2(包含检查点内容部分)*/

//对系统登录结果进行检查
if (atoi(lr_eval_string("{reLogin_Count}")) == 0)
{    //登录失败
    lr_error_message("登录失败!!!--UserName:%s",lr_eval_string("{UserName}"));
     //勾选Fail Open Transations on lr_error_message
     //当执到该lr_error_message时,"登录系统"的Transaction失败
}

lr_end_transaction("登录系统", LR_AUTO);

以上脚本调试成功后,通过检查点函数及日志信息可以判断出系统登录操作已通过脚本回放成功完成。

理论上,只要VuGen采用不同帐号迭代运行该脚本,由于只是进行系统登录操作而未进行系统注销或退出操作,业务系统中的在线用户数将持续增加。

然而在该OA系统中采用此方法时发现,虽然脚本成功运行,但业务系统中的在线用户数并未增长。这说明LoadRunner与浏览器在访问系统的过程中存在差异性。

脚本及场景设计

针对上面的问题,通过抓包工具Fiddler2对系统进行网络流量抓包 分析可知:用户登录系统后,在未进行任何操作的情况下,浏览器与服务器会定期(间隔30秒)进行通讯交互。如下图所示:

Online-Users-Communication-between-Browser-and-Server

这就解释了为什么用户在浏览器中登录系统后可以长期地保持在线,而通过脚本成功地进行系统登录后却无法保持在线状态;因为VuGen不会像浏览器那样定期地与服务器进行通讯交互。

找出其中的差异后,我们便可在VuGen中用脚本模拟浏览器的定期交互功能,简单的实现方法如下所示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
while(1){
    web_url("userAction.struts",
        "URL=http://10.147.15.28:9001/userAction.struts?actionType=refreshDynaInfo",
        "Resource=0",
        "RecContentType=text/html",
        "Referer=http://10.147.15.28:9001/jsp/oa/infocomm/sms/sysShortMsg/sysShortMsgReflesh.jsp",
        "Snapshot=t4.inf",
        "Mode=HTML",
        LAST);

    web_url("userAction.struts_2",
        "URL=http://10.147.15.28:9001/userAction.struts?actionType=refreshDynaInfo&time=Mon%20Aug%2012%2015:42:04%20UTC+0800%202013",
        "Resource=0",
        "RecContentType=text/html",
        "Referer=http://10.147.15.28:9001/jsp/oa/infocomm/sms/sysShortMsg/reflesh.jsp",
        "Snapshot=t5.inf",
        "Mode=HTML",
        LAST);

    lr_think_time(30);    //模拟浏览器与服务器30秒间隔的通讯交互
}

那么,将系统登录脚本和循环函数都放入Action中,在VuGen中采用迭代运行的方式可行吗?

虽然新增登录用户可以逐个进行,即使用VuGen通过Action迭代的形式采用不同帐号逐个地进行登录操作。但由于用户登录后需要持续间隔地与服务器进行通讯交互才能保持在线,而单线程脚本运行至while(1)后便进入死循环,从而使得Action迭代无效。因此,采用VuGen进行Action迭代的方式是不可行的。

正确的做法是,在Controller中采用逐步加载的方式,使各个虚拟用户独立地运行,从而保证了各虚拟用户登录成功后保持在线状态。

在Controller中的具体配置如下所示:

  • Run Logic:Number of Iterations设置为1
  • Think Time:Replay think time As recorded
  • Continue on error:False
  • Fail open transactions on lr_error_message:True
  • Schedule:Initialize each Vuser just before it runs
  • Schedule:Start 1000 Vusers – 1 Vuser every 3 Seconds
  • Schedule:Run for 5 minutes

通过以上配置,可以达到如下效果:

  • 每个Vuser只登录一次,然后定期30秒与服务器进行一次交互,保持在线状态
  • Vuser的登录操作与定期刷新操作不会对服务器造成并发压力,且符合真实业务场景
  • 逐步缓慢地增加在线用户数,当系统出现异常时即可查看到当前的在线用户数目

测试方法优化

通过以上方法可以测试得到业务系统所能承受的“初略的”最大在线用户数目。为什么说是“初略的”呢?因为该方法仍存在缺陷,主要体现在如下两个方面:

  • 该方法只适用于测试期间无他人使用系统的情况。如果测试期间同时有其他用户登录系统,或者系统中本身已存在在线用户,则会造成测试得到的结果不准确。
  • 该方法忽略了系统稳定性对在线用户数的影响。举例来说,也许逐步增加在线用户数至500时,系统并没有发生异常,但这并不意味着500个用户长时间处于在线状态时系统不会出现异常。

针对以上两方面缺陷,可以做出如下改进:

  • 在逐步增加在线用户数的时候,定期(比如间隔3秒)查看业务系统自身统计的在线用户数目,并以该数据为测试结果。
  • 利用之前的方法测试得到业务系统“初略的”最大在线用户数后,使系统长时间保持该数量的在线用户数目,观察系统在长时间运行期间是否会出现异常;若出现异常后,适当减少在线用户数目后重复地进行测试,直到系统可以保持长时间地稳定运行为止,此时对应的在线用户数目即为业务系统所能承受的最大在线用户数目。

在本文提到的OA系统中,对链接[http://10.147.15.28:9001/countAction.struts?actionType=listOnlineUser]进行请求可以返回得到当前系统在线用户数目的统计信息。对应地,设计如下脚本,即可实现对系统实时在线用户数目的查看。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Action()
{

    web_add_cookie("USERORGID=db93a2M11f6719ff92Mf528764d624db129b32c21fbca0cb8d6; DOMAIN=10.147.15.28");

    while(1){

        lr_start_transaction("查看在线用户数目");

        web_reg_save_param("OnlineUsers",
                "LB=[在线用户数/总用户数:<font color=\"red\">",
                "RB=</font>]",
                "ORD=2",
                "Notfound=error",
                "Search=Body",
                LAST);

        web_url("countAction.struts",
            "URL=http://10.147.15.28:9001/countAction.struts?actionType=listOnlineUser",
            "Resource=0",
            "RecContentType=text/html",
            "Referer=",
            "Snapshot=t1.inf",
            "Mode=HTML",
            LAST);

        lr_output_message("当前在线用户数目:%s", lr_eval_string("{OnlineUsers}"));

        lr_end_transaction("查看在线用户数目", LR_AUTO);

        lr_think_time(3);

    }

    return 0;
}