欢迎测试同仁一起交流!

发布新日志

  • 谈谈LoadRunner中Pacing的设置[转]

    2009-08-13 14:02:33

    在 LoadRunner 的运行场景中,有一个不大起眼的设置,可能经常会被很多人忽略,它就是 Pacing 。具体设置方式为: Run-Time settings à General à Pacing ,这个设置的功能从字面上就很容易理解,即在场景的两次迭代 (iteration) 之间,加入一个时间间隔(步进)。设置方法也很简单,这里就不赘述了,我在这里想说明的是,这个设置到底有什么作用?为什么要进行这个设置?说实话,虽然我在以前做过的一些性能测试中,偶尔会对这个步进值进行一些设置,但其实对它的真正含义和作用,我还并不十分清楚。
      前段时间,我在对X银行招聘信息系统进行性能测试的时候,发现这个值的设置对于测试的结果有着很大的影响,很遗憾当时没有深入研究这个问题,而只是简单地认为它同脚本中的 thinktime 一样只是为了更真实地模拟实际情况而已。最近在网络上看到一篇题为《调整压力测试工具》的文章,读完之后,再用之前我的测试经历加以印证,真有种豁然开朗的感觉。以下就将我的一些体会与大家分享:
      通常我们在谈到一个软件的“性能”的时候,首先想到的就是“响应时间”和“并发用户数”这两个概念。我们看到的性能需求经常都是这样定义的:
    “要求系统支持 100 个并发用户”
      看到这样的性能需求,我们往往会不假思索地就在测试场景中设置 100 个用户,让它们同时执行某一个测试脚本,然后观察其操作的响应时间,我们都是这样做的,不是吗?我在实际实施性能测试的过程中,也往往都是这样做的。可惜的是,我们中的大多数人很少去更深入地思考一下其中的奥妙,包括我自己。
      事实上,评价一个软件系统的性能,可以从两个不同的视角去看待:客户端视角和服务器视角(也有人把它叫做用户视角和系统视角),与此相对应的,又可以引出两个让初学者很容易混淆的两个概念:“并发用户数”和“每秒请求数”。“并发用户数”是从客户端视角去定义的,而“每秒请求数”则是从服务器视角去定义的。
      因此,上面所描述的做法的局限性就是,它反映的仅仅是客户端的视角。
      对于这个世界上的很多事情,变换不同的角度去看它,往往可以有助于我们得到更正确的结论。现在,我们就转换一下角度,以服务器的视角来看看性能需求应该怎么样定义: “要求系统的事务处理能力达到 100 个 / 秒” ( 这里为了理解的方便,假定在测试脚本中的一个事务仅仅包含一次请求 )
      面对以这样方式提出的性能需求,在 LoadRunner 中,我们又该如何去设置它的并发用户数呢?千万不要想当然地以为设置了 100 个并发用户数,它就会每秒向服务器提交 100 个请求,这是两个不同的概念,因为 LoadRunner 模拟客户端向服务器发出请求,必须等待服务器对这个请求做出响应,并且客户端收到这个响应之后,才会重新发出新的请求,而服务器对请求的处理是需要一个时间的。我们换个说法,对于每个虚拟用户来说,它对服务器发出请求的频率将依赖于服务器对这个请求的处理时间。而服务器对请求的处理时间是不可控的,如果我们想要在测试过程中维持一个稳定的每秒请求数( RPS ),只有一个方法,那就是通过增加并发用户数的数量来达到这个目的。这个方法看起来似乎没有什么问题,如果我们在测试场景中只执行一次迭代的话。然而有经验的朋友都会知道,实际情况并不是这样,我们通常会对场景设置一个持续运行时间(即多次迭代),通过多个事务 (transaction) 的取样平均值来保证测试结果的准确性。测试场景以迭代的方式进行,如果不设置步进值的话,那么对于每个虚拟用户来说,每一个发到服务器的请求得到响应之后,会马上发送下一次请求。同时,我们知道, LoadRunner 是以客户端的角度来定义“响应时间”的 ,当客户端请求发出去后, LoadRunner 就开始计算响应时间,一直到它收到服务器端的响应。这个时候问题就产生了:如果此时的服务器端的排队队列已满,服务器资源正处于忙碌的状态,那么该请求会驻留在服务器的线程中,换句话说,这个新产生的请求并不会对服务器端产生真正的负载,但很遗憾的是,该请求的计时器已经启动了,因此我们很容易就可以预见到,这个请求的响应时间会变得很长,甚至可能长到使得该请求由于超时而失败。等到测试结束后,我们查看一下结果,就会发现这样一个很不幸的现象:事务平均响应时间很长,最小响应时间与最大响应时间的差距很大,而这个时候的平均响应时间,其实也就失去了它应有的意义。也就是说,由于客户端发送的请求太快而导致影响了实际的测量结果。
      因此,为了解决这个问题,我们可以在每两个请求之间插入一个间隔时间,这将会降低单个用户启动请求的速度。间歇会减少请求在线程中驻留的时间,从而提供更符合现实的响应时间。这就是我在文章开头所提到的 Pacing 这个值的作用。
      最后再补充一句话:虽然性能测试通常都是从客户端活动的角度定义的,但是它们应该以服务器为中心的视角来看待。请注意这句话,理解它很重要,只有真正理解了这句话,你才会明白为什么我们一直强调做性能测试的时候要保证一个独立、干净的测试环境,以及一个稳定的网络,因为我们希望评价的是软件系统真正的性能,所以必须排除其它一切因素对系统性能造成的影响。
      花了几天的时间才完成这篇文章,如果它能够帮助大家对性能测试多一些理解或者多一些思考,那就是我的荣幸了。
  • 负载压力测试动态参数关联详解(网络转载)

    2008-12-10 11:33:09

    1.为什么需要动态参数关联

      之所以需要动态关联的原因是在系统交互过程中,服务器会动态的生成一些动态的信息给客户端,客户端需要将接受到的动态信息原封不动或者经过相应的处理再次返回给服务器,通过这样的交互,服务器可以认定客户端的可靠性或者适应业务的需要。比如session的动态信息;OA系统中发布新的公文产生的新的流水号;C/S应用中服务器可能动态的给客户端分配新的端口号。

      2.LR中动态关联的原理

      在HTTP的LR脚本中,LR实现动态关联的机制是在服务器返回的信息中匹配查找一些特征串信息,然后将找到的特征串保存在变量中,在后续的业务操作中,LR将保存的变量值提交给服务器,从而实现了动态信息的关联,联动,保证LR的脚本回放过程中使用的是新的动态参数,而不是历史的脏数据信息。

      在C/S的LR脚本中,LR提供相关的函数,可以将从服务器接收到的buffer中的部分内容保存在变量中,替换后续的变量信息,就可以实现动态参数的关联了。

      3.B/S动态关联的方法

      B/S动态关联的方法可以分为自动关联和手动关联两种。

      自动关联就是LR中已经默认的定义了一系列的动态参数查找匹配的规则,录制的脚本回放后,LR会自动的识别一些动态参数,自动的修改脚本,添加相关的脚本关联函数,实现动态参数的关联;

      手动的关联是在LR不能自动关联的情况下被迫进行的相关的手动修改脚本的方式,同样实现自动关联的效果。

      4.自动关联的实现

      自动关联在回放脚本后,LR会自动弹出提示对话框,询问是否进行correlation(自动关联),确认后LR会自动比对,查找出脚本中需要自动关联的参数位置,供测试人员选择确认参数是否需要自动关联;

      5.手动脚本的关联实现

      明确脚本中需要关联的参数,观察的细节一般是脚本中频繁出现的一些变量,比如脚本中的web_submit_data提交的客户端请求中包含的一些参数,比较两次录制的脚本,可能会发现里面的某些变量的参数值发生变化,这种情况一般需要引起关注,是否存在动态参数的关联。另一种方式可能需要和开发人员交流,在业务的交互中,是否存在某些动态的验证参数数据。

      动态参数的查找位置一般是提交动态参数前的web交互中的服务器响应中查找,可以将LR的视图切换到TREEVIEW视图下,测试人员可以看到每一个web交互过程中的服务器响应的详细数据信息以及客户端提交的web请求信息。点击查看示意图

      定位到要找的动态参数的服务器响应位置后,下面的操作是进行手动的关联,将动态参数值保存在变量中。首先在返回动态参数值的web请求前注册一个变量,使用web_reg_save_param注册一个变量,可以详细的查看一下该函数的帮助信息。在LR的TREEVIEW视图下,用户可以使用GUI交互的方式很方便的注册一个变量,用来保存动态参数。步骤如下:

      在返回参数的web请求上点右键:insertbefore–》service->web_reg_save_param,填写相应的参数特征信息,解释一下,左右边界(左右边界是动态参数值的左边和右边的特征串,LR就是通过左右边界来唯一的找到web请求的服务器响应数据中的动态参数的。)另外一个比较重要的地方就是部分特殊字符的转义问题,如果在左右边界中出现了特殊字符,如引号,在出现问题是需要考虑这个环节。

      确认以后,测试人员可以在scrīpteview视图中看到LR自动添加的参数注册函数。

      测试人员可以使用lr_error_message函数来打印调试信息,检查是否正确的捕获了动态参数;方式如下:

    lr_error_message(“theparamvalueis%s”,lr_eval_string(“{变量名}”));

      这样,在脚本的回放中,用户可以看到红色的打印输出信息。

      最后的步骤就是将脚本中的历史动态参数信息替换成已经注册的变量,这样LR就可以自动的提交动态参数值了,而不是提交不进行修改的历史数据信息。替换方法是将以前的值替换成{变量名}的方式即可。

  • 负载压力测试动态参数关联详解(网络转载)

    2008-12-10 11:33:08

    1.为什么需要动态参数关联

      之所以需要动态关联的原因是在系统交互过程中,服务器会动态的生成一些动态的信息给客户端,客户端需要将接受到的动态信息原封不动或者经过相应的处理再次返回给服务器,通过这样的交互,服务器可以认定客户端的可靠性或者适应业务的需要。比如session的动态信息;OA系统中发布新的公文产生的新的流水号;C/S应用中服务器可能动态的给客户端分配新的端口号。

      2.LR中动态关联的原理

      在HTTP的LR脚本中,LR实现动态关联的机制是在服务器返回的信息中匹配查找一些特征串信息,然后将找到的特征串保存在变量中,在后续的业务操作中,LR将保存的变量值提交给服务器,从而实现了动态信息的关联,联动,保证LR的脚本回放过程中使用的是新的动态参数,而不是历史的脏数据信息。

      在C/S的LR脚本中,LR提供相关的函数,可以将从服务器接收到的buffer中的部分内容保存在变量中,替换后续的变量信息,就可以实现动态参数的关联了。

      3.B/S动态关联的方法

      B/S动态关联的方法可以分为自动关联和手动关联两种。

      自动关联就是LR中已经默认的定义了一系列的动态参数查找匹配的规则,录制的脚本回放后,LR会自动的识别一些动态参数,自动的修改脚本,添加相关的脚本关联函数,实现动态参数的关联;

      手动的关联是在LR不能自动关联的情况下被迫进行的相关的手动修改脚本的方式,同样实现自动关联的效果。

      4.自动关联的实现

      自动关联在回放脚本后,LR会自动弹出提示对话框,询问是否进行correlation(自动关联),确认后LR会自动比对,查找出脚本中需要自动关联的参数位置,供测试人员选择确认参数是否需要自动关联;

      5.手动脚本的关联实现

      明确脚本中需要关联的参数,观察的细节一般是脚本中频繁出现的一些变量,比如脚本中的web_submit_data提交的客户端请求中包含的一些参数,比较两次录制的脚本,可能会发现里面的某些变量的参数值发生变化,这种情况一般需要引起关注,是否存在动态参数的关联。另一种方式可能需要和开发人员交流,在业务的交互中,是否存在某些动态的验证参数数据。

      动态参数的查找位置一般是提交动态参数前的web交互中的服务器响应中查找,可以将LR的视图切换到TREEVIEW视图下,测试人员可以看到每一个web交互过程中的服务器响应的详细数据信息以及客户端提交的web请求信息。点击查看示意图

      定位到要找的动态参数的服务器响应位置后,下面的操作是进行手动的关联,将动态参数值保存在变量中。首先在返回动态参数值的web请求前注册一个变量,使用web_reg_save_param注册一个变量,可以详细的查看一下该函数的帮助信息。在LR的TREEVIEW视图下,用户可以使用GUI交互的方式很方便的注册一个变量,用来保存动态参数。步骤如下:

      在返回参数的web请求上点右键:insertbefore–》service->web_reg_save_param,填写相应的参数特征信息,解释一下,左右边界(左右边界是动态参数值的左边和右边的特征串,LR就是通过左右边界来唯一的找到web请求的服务器响应数据中的动态参数的。)另外一个比较重要的地方就是部分特殊字符的转义问题,如果在左右边界中出现了特殊字符,如引号,在出现问题是需要考虑这个环节。

      确认以后,测试人员可以在scrīpteview视图中看到LR自动添加的参数注册函数。

      测试人员可以使用lr_error_message函数来打印调试信息,检查是否正确的捕获了动态参数;方式如下:

    lr_error_message(“theparamvalueis%s”,lr_eval_string(“{变量名}”));

      这样,在脚本的回放中,用户可以看到红色的打印输出信息。

      最后的步骤就是将脚本中的历史动态参数信息替换成已经注册的变量,这样LR就可以自动的提交动态参数值了,而不是提交不进行修改的历史数据信息。替换方法是将以前的值替换成{变量名}的方式即可。

  • Loadrunner中web_reg_save_param的使用详解(转)

    2008-11-26 15:40:31

    应用范围
      在使用Loadrunner进行性能测试时,经常遇到一种情况,需要通过web页面修改某事务的状态。于是需要首先读出当前的事务的状态,再进行修改,此时便可以使用到web_reg_save_param了。可以通过它先将事务的状态读出写入一个自定义的变量中,根据变量的值来决定下一步的动作。

     简要说明

      语法:int web_reg_save_param(const char *ParamName, <list of Attributes>, LAST);

     参数说明:
    ParamName: 存放得到的动态内容的参数名称
    list of Attributes: 其它属性,包括:Notfound, LB, RB, RelFrameID, Search, ORD, SaveOffset, Convert, SaveLen。属性值不分大小写
    Notfound: 当在返回信息中找不到要找的内容时应该怎么处理
    Notfound=error: 当在返回信息中找不到要找的内容时,发出一个错误讯息。这是缺省值。
    Notfound=warning: 当在返回信息中找不到要找的内容时,只发出警告,脚本也会继续执行下去不会中断。
    LB( Left Boundary ) : 返回信息的左边界字串。该属性必须有,并且区分大小写。
    RB( Right Boundary ): 返回信息的右边界字串。该属性必须有,并且区分大小写。
    RelFrameID: 相对于URL而言,欲查找的网页的Frame。此属性质可以是All或是数字,该属性可有可无。
    Search : 返回信息的查找范围。可以是Headers,Body,Noresource,All(缺省)。该属性质可有可无。
    ORD : 说明第几次出现的左边界子串的匹配项才是需要的内容。该属性可有可无,缺省值是1。如为All,则将所有找到的内容储存起来。
    SaveOffset : 当找到匹配项后,从第几个字元开始存储到参数中。该属性不能为负数,缺省值为0。
    SaveLen :当找到匹配项后,偏移量之后的几个字元存储到参数中。缺省值是-1,表示一直到结尾的整个字串都存入参数。
    Convert : 可取的值有以下两种:
      HTML_TO_URL : 将 HTML-encoded 资料转成 URL-encoded 资料格式
      HTML_TO_TEXT : 将 HTML-encoded 资料转成纯文字资料格式

     实例讲解
      目的:取得页面中的商品状态,如果状态是正常态就改为注销态,否则改为正常态。

     录制脚本使用的是URL based scrīpt

     
     
      将返回的数据记录到日志

     

      直接手工访问页面,检查URL

     

      该页面上点击右键,选择属性

     

      看到URL,对照录制下的脚本中有:

    web_url("modifyOfferingStatePage.do",
    "URL={url}/web/businessAccept/order/modifyOfferingStatePage.do?offeringId=
    282172&offeringSpecId=1&offeringSpecName=普通宽带(ADSL/LAN)&customerName=
    {clientname}&nodeId=260000&pos1=定购管理&pos2=修改商品状态",

    "Resource=0",
    "RecContentType=text/html",
    "Referer={url}/web/businessAccept/order/orderMenu.do",
    "Snapshot=t23.inf",
    "Mode=HTTP",
    LAST);

     于是在这段代码前添加注册函数:

    web_reg_save_param("oldstate",
    "LB/IC=原有商品状态:</td>",
    "RB/IC=</td>",
    "Search=body",
    "Ord=1",
    "RelFrameId=1",
    "SaveOffset=57",
    "SaveLen=4",
    LAST);
    web_url("modifyOfferingStatePage.do",
    "URL={url}/web/businessAccept/order/modifyOfferingStatePage.do?offeringId=
    282172&offeringSpecId=1&offeringSpecName=
    普通宽带(ADSL/LAN)&customerName={clientname}&nodeId=
    260000&pos1=定购管理&pos2=修改商品状态",

    "Resource=0",
    "RecContentType=text/html",
    "Referer={url}/web/businessAccept/order/orderMenu.do",
    "Snapshot=t23.inf",
    "Mode=HTTP",
    LAST);
    ...............
    //将得到的内容存入日志用于检查
    lr_log_message("getvalue : %s",lr_eval_string ("{oldstate}"));

    if ( lr_eval_string ("{oldstate}") == "正常"){
    web_submit_data("modifyOfferingState.do",
    "Action={url}/web/businessAccept/order/modifyOfferingState.do",
    "Method=POST",
    "RecContentType=text/html",
    "Referer={url}/web/businessAccept/order/modifyOfferingStatePage.do?offeringId=
    282172&offeringSpecId=1&offeringSpecName=普通宽带(ADSL/LAN)&customerName=
    {clientname}&nodeId=260000&pos1=定购管理&pos2=修改商品状态",

    "Snapshot=t24.inf",
    "Mode=HTTP",
    ITEMDATA,
    "Name=offering.state", "Value=1", ENDITEM,
    "Name=offering.recentModifyReason", "Value=修改原因", ENDITEM,
    "Name=offering.customerId", "Value=281218", ENDITEM,
    "Name=offering.offeringId", "Value=282172", ENDITEM,
    "Name=offering.offeringSpecId", "Value=1", ENDITEM,
    "Name=offering.recentMender", "Value=root", ENDITEM,
    "Name=offering.recentModifyDatetime", "Value=2005-01-16", ENDITEM,
    "Name=nodeId", "Value=260000", ENDITEM,
    "Name=customerName", "Value={clientname}", ENDITEM,
    "Name=offeringSpecName", "Value=普通宽带(ADSL/LAN)", ENDITEM,
    "Name=submit.x", "Value=33", ENDITEM,
    "Name=submit.y", "Value=13", ENDITEM,
    LAST);
    }
    Else
    {
    web_submit_data("modifyOfferingState.do",
    "Action={url}/web/businessAccept/order/modifyOfferingState.do",
    "Method=POST",
    "RecContentType=text/html",
    "Referer={url}/web/businessAccept/order/modifyOfferingStatePage.do?offeringId=
    282172&offeringSpecId=1&offeringSpecName=普通宽带(ADSL/LAN)&customerName=
    {clientname}&nodeId=260000&pos1=定购管理&pos2=修改商品状态",

    "Snapshot=t24.inf",
    "Mode=HTTP",
    ITEMDATA,
    "Name=offering.state", "Value=0", ENDITEM,
    "Name=offering.recentModifyReason", "Value=修改原因", ENDITEM,
    "Name=offering.customerId", "Value=281218", ENDITEM,
    "Name=offering.offeringId", "Value=282172", ENDITEM,
    "Name=offering.offeringSpecId", "Value=1", ENDITEM,
    "Name=offering.recentMender", "Value=root", ENDITEM,
    "Name=offering.recentModifyDatetime", "Value=2005-01-16", ENDITEM,
    "Name=nodeId", "Value=260000", ENDITEM,
    "Name=customerName", "Value={clientname}", ENDITEM,
    "Name=offeringSpecName", "Value=普通宽带(ADSL/LAN)", ENDITEM,
    "Name=submit.x", "Value=33", ENDITEM,
    "Name=submit.y", "Value=13", ENDITEM,
    LAST);
    }
    从日志中截取的真实的返回内容为:
    vuser_init.c(689): <tr bgcolor="#F6F6F6">\r\n
    vuser_init.c(689): <td width="30%" height="23" align="right">\r\n
    vuser_init.c(689): 原有商品状态:</td>\r\n
    vuser_init.c(689): <td width="70%" height="23"> 正常 </td>\r\n
    vuser_init.c(689): </tr>\r\n
    vuser_init.c(689): <tr bgcolor="#F4FBFE">\r\n
    vuser_init.c(689): <td width="30%" height="23" align="right">\r\n
    vuser_init.c(689): 修改后的状态:</td>\r\n
    vuser_init.c(689): <td width="70%" height="23">\r\n
    vuser_init.c(689): \r\n
    vuser_init.c(689): \r\n
    vuser_init.c(689): \r\n
    vuser_init.c(689): <input type="radio" name='offering.state' value='4' checked>
    可以看到左边界是:原有商品状态:</td>,
    右边界是:</td>,偏移量为:57(包括了空格),
    长度为:4(因为一个汉字长度为2),最后存入变量的值是:正常

     4.经验总结
      1)为了便于脚本的调试,将返回的数据都写入日志是个好办法;
      2)为了验证取得的数据是否是自己期望的,可以将取得的数据写入日志中进行验证,
      例:lr_log_message("getvalue : %s",lr_eval_string ("{oldstate}"));
      3)因为它是一个注册函数,必须在返回信息前使用,所以注册的位置必须正确,否则很可能得到类似如下错误:
      4)vuser_init.c(734): Error -27190: No match found for the requested parameter "oldstate".
      Check whether the requested boundaries exist in the response data. Also,
      if the data you want to save exceeds 1024 bytes,
      use web_set_max_html_param_len to increase the parameter size [MsgId: MERR-27190]
      5)vuser_init.c(734): Error -27187: The above "not found"
      error(s) may be explained by header and body byte counts being 0 and 0,
      respectively. [MsgId: MERR-27187]
      6)vuser_init.c(734):
      web_concurrent_end highest severity level was "ERROR" [MsgId: MMSG-27181]
      7)所以使用手工方法,右键页面确定在代码中哪个位置之前注册函数至关重要
      8)如果脚本中中文为乱码,可能是因为源文件的字符集和操作系统字符集不匹配。试试:

  • 如何在 LoadRunner 脚本中做关联 (Correlation)(转)

    2008-11-26 14:53:41

    如何在 LoadRunner 脚本中做关联 (Correlation)
    当录制脚本时,VuGen会拦截client端(浏览器)与server端(网站服务器)之间的对话,并且通通记录下来,产生脚本。在VuGen 的Recording Log中,您可以找到浏览器与服务器之间所有的对话,包含通讯内容、日期、时间、浏览器的请求、服务器的响应内容等等。脚本和Recording Log最大的差别在于,脚本只记录了client端要对server端所说的话,而Recording Log则是完整纪录二者的对话。

    当执行脚本时,您可以把VuGen想象成是一个演员,它伪装成浏览器,然后根据脚本,把当初真的浏览器所说过的话,再对网站伺服器重新说一遍,VuGen企图骗过服务器,让服务器以为它就是当初的浏览器,然后把网站内容传送给VuGen。
    所以纪录在脚本中要跟服务器所说的话,完全与当初录制时所说的一样,是写死的(hard-coded)。这样的作法在遇到有些比较聪明的服务器时,还是会失效。这时就需要透过「关联(correlation)」的做法来让VuGen可以再次成功地骗过服务器。
    何谓关联(correlation)?
    所谓的关联(correlation)就是把脚本中某些写死的(hard-coded)数据,转变成是撷取自服务器所送的、动态的、每次都不一样的数据。
    举一个常见的例子,刚刚提到有些比较聪明的服务器,这些服务器在每个浏览器第一次跟它要数据时,都会在数据中夹带一个唯一的辨识码,接下来就会利用这个辨识码来辨识跟它要数据的是不是同一个浏览器。一般称这个辨识码为Session ID。对于每个新的交易,服务器都会产生新的Session ID给浏览器。这也就是为什么执行脚本会失败的原因,因为VuGen还是用旧的Session ID向服务器要数据,服务器会发现这个Session ID是失效的或是它根本不认识这个Session ID,当然就不会传送正确的网页数据给VuGen了。
    下面的图示说明了这样的情形:
    当录制脚本时,浏览器送出网页A的请求,服务器将网页A的内容传送给浏览器,并且夹带了一个ID=123的数据,当浏览器再送出网页B的情求时,这时就要用到ID=123的数据,服务器才会认为这是合法的请求,并且把网页B的内容送回给浏览器。
    在执行脚本时会发生什么状况?浏览器再送出网页B的请求时,用的还是当初录制的ID=123的数据,而不是用服务器新给的ID=456,整个脚本的执行就会失败。

    要对付这种服务器,我们必须想办法找出这个Session ID到底是什么、位于何处,然后把它撷取下来,放到某个参数中,并且取代掉脚本中有用到Session ID的部份,这样就可以成功骗过服务器,正确地完成整个交易了。
    哪些错误代表着我应该做关联(correlation)?
    假如脚本需要关联(correlation),在还没做之前是不会执行通过的,也就是说会有错误讯息发生。不过,很不幸地,并没有任何特定的错误讯息是和关联(correlation)有关系的。会出现什么错误讯息,与系统实做的错误处理机制有关。错误讯息有可能会提醒您要重新登入,但是也有可能直接就显示HTTP 404的错误讯息。
    要如何做关联(correlation)?
    关联(correlation)函数
    关联(correlation)会用到下列的函数:
    • web_reg_save_param:这是最新版,也是最常用来做关联(correlation)的函数。
    语法:
    web_reg_save_param ( “Parameter Name” , < list of Attributes >, LAST );
    • web_create_html_param、web_create_html_param_ex:这二个函数主要是保留作为向前兼容的目的的。建议使用 web_reg_save_param 函数。
    详细用法请参考使用手册。在VuGen中点选【Help】>【Function reference】>【Contexts】>【Web and Wireless Vuser Functions】>【Correlation Functions】。
    如何找出要关联(correlation)数据
    简单的说,每一次执行时都会变动的值,就有可能需要做关联(correlation)。
    VuGen提供二种方式帮助您找出需要做关联(correlation)的值:
    1. 自动关联
    2. 手动关联
    自动关联
    VuGen内建自动关联引擎(auto-correlation engine),可以自动找出需要关联的值,并且自动使用关联函数建立关联。
    自动关联提供下列二种机制:
    • Rules Correlation:在录制过程中VuGen会根据订定的规则,实时自动找出要关联的值。规则来源有两种:
    o 内建(Built-in Correlation):
    VuGen已经针对常用的一些应用系统,如AribaBuyer、BlueMartini、BroadVision、InterStage、 mySAP、NetDynamics、Oracle、PeopleSoft、Siebel、SilverJRunner等,内建关联规则,这些应用系统可能会有一种以上的关联规则。您可以在【Recording Options】>【Internet Protocol】>【Correlation】中启用关联规则,则当录制这些应用系统的脚本时,VuGen会在脚本中自动建立关联。
    您也可以在【Recording Options】>【Internet Protocol】>【Correlation】检视每个关联规则的定义。
    o 使用者自订(User-defined Rules Correlation):
    除了内建的关联规则之外,使用者也可以自订关联规则。您可以在【Recording Options】>【Internet Protocol】>【Correlation】建立新的关联规则。
    • Correlation Studio:有别于Rules Correlation,Correlation Studio则是在执行脚本后才会建立关联,也就是说当录制完脚本后,脚本至少须被执行过一次,Correlation Studio才会作用。Correlation Studio会尝试找出录制时与执行时,服务器响应内容的差异部分,藉以找出需要关联的数据,并建立关联。
    Rule Correlation
    请依照以下步骤使用Rule Correlation:
    1. 启用auto-correlation
    1. 点选VuGen的【Tools】>【Recording Options】,开启【Recording Options】对话窗口,选取【Internet Protocol】>【Correlation】,勾选【Enable correlation during recording】,以启用自动关联。
    2. 假如录制的应用系统属于内建关联规则的系统,如AribaBuyer、BlueMartini、BroadVision、InterStage、 mySAP、NetDynamics、Oracle、PeopleSoft、Siebel、SilverJRunner等,请勾选相对应的应用系统。
    3. 或者也可以针对录制的应用系统加入新的关联规则,此即为使用者自订的关联规则。
    4. 设定当VuGen侦测到符合关联规则的数据时,要如何处理:
     【Issue a pop-up message and let me decide online】:跳出一个讯息对话窗口,询问您是否要建立关联。
     【Perform correlation in sceipt】:直接自动建立关联
    2. 录制脚本
    开始录制脚本,在录制过程中,当VuGen侦测到符合关联规则的数据时,会依照设定建立关联,您会在脚本中看到类似以下的脚本,此为BroadVision应用系统建立关联的例子,在脚本批注部分可以看到关联前的数据为何。

    3. 执行脚本验证关联是OK的。
    Correlation Studio
    当录制的应用系统不属于VuGen预设支持的应用系统时,Rule Correlation可能既无法发挥作用,这时可以利用Correlation Studio来做关联。
    Correlation Studio会尝试找出录制时与执行时,服务器响应内容的差异部分,藉以找出需要关联的数据,并建立关联。
    使用Correlation Studio的步骤如下:
    1. 录制脚本并执行
    2. 执行完毕后,VuGen会跳出下面的【Scan Action for Correlation】窗口,询问您是否要扫描脚本并建立关联,按下【Yes】按钮。

    3. 扫描完后,可以在脚本下方的【Correlation Results】中看到扫描的结果。

    4. 检查一下扫瞄的结果后,选择要做关联的数据,然后按下【Correlate】按钮,一笔一笔做,或是按下【Correlate All】让VuGen一次就对所有的数据建立关联。
    注意:由于Correlation Studio会找出所有有变动的数据,但是并不是所有的数据都需要做关联,所以不建议您直接用【Correlate All】。
    5. 一般来说,您必须一直重复步骤1~4直到所有需要做关联的数据都找出来为止。因为有时前面的关联还没做好之前,将无法执行到后面需要做关联的部份。
    有可能有些需要做关联的动态数据,连Correlation Studio都无法侦测出来,这时您就需要自行做手动关联了。
    手动关联
    手动关联的执行过程大致如下:
    1. 使用相同的业务流程与数据,录制二份脚本
    2. 使用WinDiff工具协助找出需要关联的数据
    3. 使用web_reg_save_param函数手动建立关联
    4. 将脚本中有用到关联的数据,以参数取代
    接下来将详细的说明如何执行每个步骤
    使用相同的业务流程与数据,录制二份脚本
    1. 先录制一份脚本并存档。
    2. 依照相同的操作步骤与数据录制第二份脚本并存盘。注意,所有的步骤和输入的数据一定都要一样,这样才能找出由服务器端产生的动态数据。
    有时候会遇到真的无法使用相同的输入数据,那您也要记住您使用的输入数据,到时才能判断是您输入的数据,还是变动的数据。
    使用WinDiff工具协助找出需要关联的数据
    1. 在第二份脚本中,点选VuGen的【Tools】>【Compare with Vuser…】,并选择第一份脚本。
    2. 接着WinDiff会开启,同时显示二份脚本,并显示有差异的地方。WinDiff会以一整行黄色标示有差异的脚本,并且以红色的字体显示真正差异的文字。(假如没看到红色字体,请点选【Options】>【View】>【Show Inline Differences】)。
    3. 逐一检视二份脚本中差异的部份,每一个差异都可能是需要做关联的地方。选取差异的脚本,然后复制。
    在复制时,有时并不需要取整行脚本,可能只会选取脚本中的一部分。
    注意:请忽略lr_thik_time的差异部份,因为lr_thik_time是用来模拟每个步骤之间使用者思考延迟的时间。

    4. 接着要在Recording Log(单一protocol)或是Generation Log(多重protocol)中找这个值。将鼠标光标点到Recording Log的第一行开头,按下Ctrl+F,开启【Find】窗口,贴上刚刚复制的脚本,找出在Recording Log第一次出现的位置。

    结果会有二种:
    o 在Recording Log中找不到要找的数据,这时请先确认您找对了脚本,毕竟现在开启了二个几乎一样的脚本,很容易弄错。
    o 在Recording Log中找到了要找的数据,这时要确认数据是从服务器端传送过来的。首先可以先检查数据的标头,从标头的Receiving response可以知道数据是从服务器端传送到client端的。假如此数据第一次出现是在Sending request中,则表示此数据是由client端产生,不需要做关联,但是有可能需要做参数化(parameterized)。
    您要找的标头格式如下:
    *** [tid=b9 Action1 2] Receiving response from host astra.merc-int.com:80 ( 25/11/2002 12:04:00 )

    5. 现在您已经找到录制二次都不一样,而且是由服务器所产生的动态数据了,而此数据极有可能需要做关联。
    使用web_reg_save_param函数手动建立关联
    在找到是由服务器所产生的动态数据之后,接下来要做的就是找出适当的位置,使用web_reg_save_param函数,将这个动态数据撷取到某个参数中。
    1. 要在哪里使用web_reg_save_param函数?
    在之前的步骤,我们已经在Execution Log找到可能需要关联的动态数据。在Execution Log中选取动态数据前的文字然后复制,我们将会利用这段文字,来帮助我们找出要关联的动态数据。

    不过在这之前我们要先找出使用web_reg_save_param函数的正确位置,所以我们要再重新执行一遍脚本,而且这次会开启所有的Log。
    1. 在VuGen中点选【Vuser】>【Run-Time Settings】。
    2. 点选【General】>【Log】。
    3. 勾选【Enable logging】、【Always sends messages】、【Extended log】,以及【Extended log】下的所有选项。
    4. 按下【OK】就可以执行脚本了。
    执行完脚本之后,在Execution Log中搜寻刚刚复制的字符串。找到字符串后,在字符串前面会有A.tion1.c(7),这个7就是到时候要插入web_reg_save_param函数的位置,也就是要插入到脚本的第7行。
    在脚本的第7行前插入一行空白行,然后输入
    web_reg_save_param(“UserSession”,
    “UserSession” 这个 “UserSession” 就是到时要使用的参数名称,建议给个有意义的名字。
    注意:到这里整个web_reg_save_param函数还没完成。

    2. 找出web_reg_save_param中要用到的边界
    web_reg_save_param函数主要是透过动态数据的前面和后面的固定字符串,来辨识要撷取的动态数据的,所以我们还需要找出动态数据的边界字符串。
    找出左边界字符串
    再回到Execution Log中,选取动态数据前的字符串并且复制它。
    这时会有个问题,到底要选取多少字符串才足以唯一识别要找的动态数据呢?建议是越多越好,但是尽量不要包含到特殊字符。
    在这边我们选取「input type=hidden name=userSession value=」字符串。选好之后,还要再确认一次这段字符串真的是可以唯一识别的,所以我们在Execution Log中透过Ctrl+F的搜寻,找找看这段字符串是否可以找到要找的动态数据。假如找不到,web_reg_save_param函数还有个ORD参数可以使用,ORD参数可以设定出现在第几次的字符串才是要找的字符串。
    将这个边界字符串加到未完成的web_reg_save_param函数中:
    web_reg_save_param(“UserSession”, “LB= input type=hidden name=userSession value=”,
    找出右边界字符串
    接下来要找出动态数据的右边界字符串,这个字符串就比较好找了,从动态数据的最后一个字符开始,通常就是我们要找的右边界字符串了。
    以这个例子来看,就是「>」,所以再把右边界字符串加入,web_reg_save_param函数中,这时web_reg_save_param函数已经快完成了。最后再加上「LAST);」就完成整个web_reg_save_param函数了。
    web_reg_save_param(“UserSession”, “LB= input type=hidden name=userSession value=”, “RB=>”, LAST);

    将脚本中有用到关联的数据,以参数取代
    当使用web_reg_save_param建立参数后,接下来就是用“UserSession”参数去取代脚本中写死的(hard-coded)资料。
    范例:

    “Name=userSession”, “Value=75893.0884568651DQADHfApHDHfcDtccpfAttcf”, ENDITEM,
    换成
    “Name=userSession”, “Value={UserSession}”, ENDITEM,

    到这里您已经完成了一个关联了,接下来就是执行脚本,是否能成功运行,假如还是有问题,就要检查看看是否还需要再做另一个关联。
    关于 web_reg_save_param 函数
    对于关联(correlation)来说,web_reg_save_param是最重要的一个函数,其功能是在
    下载的网页内容中,透过设定的边界字符串,找出特定的数据并将其储存在一个参数中,以供后续脚本使用。
    接下来将针对web_reg_save_param做比较详细的说明。
    Service and registration type function
    web_reg_save_param是一个Service function。service function主要是用来完成一些特殊的
    工作的,如关联、设定proxy、提供认证信息等,当其作用时,不会对网页的内容做任何的修改。
    web_reg_save_param同时也是一个registration type function (只要函数名称中包含_reg_的字眼,表示其为registration type function)。registration type function意味着其真正作用的时机是在下一个action function完成时执行的。举例来说,当某个web_url执行时所接收到的网页内容中包含了要做关联的动态数据,则必须将 web_reg_save_param放在此web_url之前,则web_reg_save_param会在web_url执行完毕后,也就是网页内容都下载完后,再执行web_reg_save_param找寻要做关联的动态数据并建立参数。
    所以要记住一点,要使用registration type function时,要注意其放置的位置必须在要作用的action function之前。
    语法
    int web_reg_save_param(const char *ParamName, <list of Attributes>, LAST);
    参数说明
    ParamName:存放动态数据的参数名称
    list of Attributes:其它属性,包含 Notfound, LB, RB, RelFrameID, Search, ORD, SaveOffset, Convert, 以及 SaveLen。属性值不分大小写,例如 Search=all。以下将详细说明每个属性值的意义:
    • Notfound:指定当找不到要找的动态数据时该怎么处置。
    o Notfound=error:当找不到动态数据时,发出一个错误讯息。假如没设定此属性,此为LoadRunner的默认值。
    o Notfound=warning:当找不到动态数据时,不发出错误讯息,只发出警告,脚本也会继续执行下去不会中断。在对角本除错时,可以使用此属性值。
    • LB:动态数据的左边界字符串。此属性质是必须要有的,而且区分大小写。
    • RB:动态数据的右边界字符串。此属性质是必须要有的,而且区分大小写。
    • RelFrameID:相对于URL而言,欲搜寻的网页的Frame。此属性质可以是All或是数字,而且可有可无。
    • Search:搜寻的范围。可以是Headers(只搜寻headers)、Body(只搜寻body部分,不搜寻header)、Noresource (只搜寻body部分,不搜寻header与resource)或是All(搜寻全部范围,此为默认值)。此属性质可有可无。
    • ORD:指明从第几次出现的左边界开始才是要撷取的数据。此属性质可有可无,默认值是1。假如值为All,则所有找到符合的数据会储存在数组中。
    • SaveOffset:当找到符合的动态数据时,从第几个字符开始才开始储存到参数中。此属性质不可为负数,其默认值为0。
    • Convert:可能的值有二种:
    o HTML_TO_URL: 将HTML-encoded数据转成URL-encoded数据格式
    o HTML_TO_TEXT:将HTML-encoded数据转成纯文字数据格式
    • SaveLen:从offect开始算起,到指定的长度内的字符串,才储存到参数中。此参数可有可无,默认值是-1,表示储存到结尾整个字符串。
    范例
    web_reg_save_param("A", "LB/ic=<a href=", "RB='>", "Ord=All", LAST);nner会搜寻网页中所有以 「<a href=」 开头,且以 「’>」结束,当中包含的字符串,并且储存在「A」参数中。
    Tips and Tricks
    以下提供一些关联的常见问题:
    • 如何打印出参数值?
    lr_output_message这二个函数来做到。例如:
    lr_output_message(“Value Captured = %s”, lr_eval_string(“{ParameterName}”));
    lr_eval_string与lr_output_message函数的使用说明请参考LoadRunner Online Function Reference。
    • 在脚本的data目录下找不到路制时的快照(snapshot)
    造成在脚本的data目录下找不到路制时的快照(snapshot)的可能原因如下:
    o 脚本是由VuGen 6.02或更早的版本所录制的
    o 汇入的Action不会包含快照(snapshot)的档案
    o 脚本是储存在只读的目录下,早成VuGen无法储存执行时撷取的快照(snapshot)
    o 某些步骤并不会产生快照(snapshot),如浏览某个资源
    o 快照(snapshot)功能被取消
    【Tools】>【General options】>【Correlation】tab >【Save correlation information during replay】
    • 开启WinDiff时出现「File no longer available」的错误讯息
    WinDiff这个工具有些限制,无法开启包含空格符的目录或是脚本,所以建议命名时不要使用空格符,并且尽可能将名称取短一点。
    • 录制时突然跳出【Correlation warning】对话窗口
    当你有勾选自动关联的【Issue a popup message and let me decide online】选项,当VuGen发现有可能要做关联的数据时,就会跳出【Correlation warning】的窗口,询问你要做关联(Correlation in scrīpt)还是要忽略(Ignore)。
    另外你也可以勾选【Perform correlation in scrīpt】,让VuGen自动作关联,不会再跳出询问窗口。
    或是勾选【Disable correlation engine】,关闭自动关联的功能。

    • 如何手动启动「Scan action for correlation」的功能
    要手动启动「Scan action for correlation」的功能,请先执行脚本一次后,点选【Vuser】>【Scan Action for Correlation】。

    • 执行完脚本后并未出现【Scan Action for Correlation】窗口
    要启用【Scan Action for Correlation】功能,请点选【Tools】>【General options】>【Correlation】tab,勾选【Show Scan for correlation popup after replay of Vuser】选项

Open Toolbar