发布新日志

  • loadrunner循环执行某个动作

    2012-05-31 16:33:45

    1action部分定义  int   i;  int   count;

    2、 打算循环的代码前代码如下:

      count=rand() % 8 +1;

     

        for(i=0;i<count;i++)

     录制的脚本;


    3、其实只需要在需要重复执行的代码前加入for循环语句即可。
  • JProfiler学习笔记(转)

    2012-04-24 19:53:55

    JProfiler学习笔记

     

    一、安装JProfiler

           http://www.ej-technologies.com/下载5.1.2并申请试用序列号

     

    二、主要功能简介

    1内存剖析 Memory profiler

    JProfiler 的内存视图部分可以提供动态的内存使用状况更新视图和显示关于内存分配状况信息的视图。所有的视图都有几个聚集层并且能够显示现有存在的对象和作为垃圾回收的对象。

    • 所有对象 
      显示类或在状况统计和尺码信息堆上所有对象的包。你可以标记当前值并显示差异值。
    • 记录对象 Record objects 
      显示类或所有已记录对象的包。你可以标记出当前值并且显示差异值。
    • 分配访问树 Allocation call tree 
      显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的J2EE组件。
    • 分配热点 Allocation hot spots 
      显示一个列表,包括方法、类、包或分配已选类的J2EE组件。你可以标注当前值并且显示差异值。对于每个热点都可以显示它的跟踪记录树。

    2堆遍历 Heap walker

    JProfiler的堆遍历器(Heap walker)中,你可以对堆的状况进行快照并且可以通过选择步骤下寻找感兴趣的对象。堆遍历器有五个视图:

    •  Classes 
      显示所有类和它们的实例。
    • 分配 Allocations 
      为所有记录对象显示分配树和分配热点。
    • 索引 References 
      为单个对象和“显示到垃圾回收根目录的路径”提供索引图的显示功能。还能提供合并输入视图和输出视图的功能。
    • 数据 Data 
      为单个对象显示实例和类数据。
    • 时间 Time 
      显示一个对已记录对象的解决时间的柱状图。

    3 CPU 剖析 CPU profiler

    JProfiler 提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或J2EE组件等不同层上。CPU视图部分包括:

    • 访问树 Call tree 
      显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。JDBC,JMSJNDI服务请求都被注释在请求树中。请求树可以根据ServletJSPURL的不同需要进行拆分。
    • 热点 Hot spots 
      显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBCJMSJNDI服务请求以及按照URL请求来进行计算。
    • 访问图 Call graph 
      显示一个从已选方法、类、包或J2EE组件开始的访问队列的图。

    4 线程剖析 Thread profiler

    对线程剖析,JProfiler提供以下视图:

    • 线程历史 Thread history 
      显示一个与线程活动和线程状态在一起的活动时间表。
    • 线程监控 Thread monitor 
      显示一个列表,包括所有的活动线程以及它们目前的活动状况。
    • 死锁探测图表 Deadlock Detection 
      显示一个包含了所有在JVM里的死锁图表。
    • 目前使用的监测器 Current monitor useage 
      显示目前使用的监测器并且包括它们的关联线程。
    • 历史检测记录 History usage history 
      显示重大的等待事件和阻塞事件的历史记录。
    • 监测使用状态 Monitor usage statistics 
      显示分组监测,线程和监测类的统计监测数据。

    5VM 遥感勘测技术 VM telemetry

    观察JVM的内部状态,JProfiler提供了不同的遥感勘测视图,如下所示:

    •  Heap 
      显示一个堆的使用状况和堆尺寸大小活动时间表。
    • 记录的对象 Recorded objects 
      显示一张关于活动对象与数组的图表的活动时间表。
    • 垃圾回收 Garbage collector 
      显示一张关于垃圾回收活动的活动时间表。
    •  Classes 
      显示一个与已装载类的图表的活动时间表。
    • 线程 Threads 
      显示一个与动态线程图表的活动时间表

     

    三、实战

    ()任务目标

    找出项目中内存增大的原因

      (二)配置说明

               操作系统:Windows2003

                  Web容器:Tomcat5.0.23

                  JDK版本:sun1.4.2

                  监控类型:本地

                  Jprofiler安装路径:D:/jprofiler5

                  Tomcat安装路径:D:/Tomcat5

    (三)  测试项目

    1.  新建WEB项目test

    2.  建包cn.test

    3.  在该包下建类文件TestMain.java  TestBean.java

    package cn.test;

    public class TestBean {

       String name = "";

    }

     

    package cn.test;

    import java.util.ArrayList;

    public class TestMain {

    public static ArrayList list = new ArrayList(); //存放对象的容器

    public static int counter = 0;    //作统计用

         }

     

           4.建测试用的JSP文件init1.jspinit2.jsp

    Init1.jsp(每次执行都创建1万个TestBean对象)

    <%@ page language="java" import="cn.test.*" pageEncoding="ISO-8859-1"%>

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

    <html>

      <head>

        <title>init</title>

      </head>

      <body><%

        for(int i=0;i<10000;i++){

           TestBean b = new TestBean();

           TestMain.list.add(b);      

        }

        %>

        SIZE:<%=TestMain.list.size()%><br/>

        counter:<%=TestMain.counter++%>

      </body>

    </html>

    Init2.jspinit1.jsp一模一样即可(后面有用)。

     

     

    (四)     配置测试用例

    1.  点击d:/jprofiler5/bin/jprofiler.exe

    2.  执行菜单SessionàIntegration WizardsàNew ServerIntegration

     

     

     

    选择是本地测试还是远程测试:

     

    选择tomcat运行的脚本文件:

     

    选择虚拟机的类型:

     

    选择监控端口:

    用默认的即可

     

    选择Web容器是否和Jprofiler一起运行:

    默认即可

     

    配置提示:

      在“远程控制”的时侯要仔细阅读一下。

     

    然后选择立即起动,开始运行。

     

     

    点击“OK”,我们可以看到另外一个小窗口出来了:

     

    Jprofiler的窗口为:

     

    这样我们就可以进行监控了!

    (五)     开始测试

    1.  IE地址栏中输入:http://localhost/test/init1.jsp,执行一次,我们可以在内存视图中看到cn.test.TestBean对象被创建了10000次:

     

    2.标记现在的状态,然后再执行init1.jsp,init2.jsp可以让我们找到哪些类在调用后没有被释放(很重要!!!)

     

    查看哪些类被发生了变化:

    红色的变成是发生变化的对象及其数量。

    我刚才执行了4init1.jsp1init2.jsp,正好产生了50000TestBean对象,和图示显示的一样。

    3.  过一会后,按F4键进行垃圾回收。但回收完成后,这些对象依然存在,说明某些地方对这个类的引用没有被释放!

    4.  找出是哪些地方使用了TestBean类,并且没有释放它们

    cn.test.TestBean对象上点击右键选择“Take Heap Snapshot for Selection”,观察它的heap

    下一步:

    点击“OK”:

     

    在该类中点击右键,在出现的菜单中选择“Use Selected Objects”:

    出现如下窗口:

    选择”Allocations”,点击“OK,然后我们要的结果就出来了

    图中显示调用此类的地方是init1.jspinit2.jsp,并且各自占用的比率都列出来了。

    既然问题的所在找出来了,接下来就该去解决问题了!

    (五)总结

    其实,我们在测试内存占用时还可以另外写一个释放内存的JSP文件来配合测试,会更清楚一些:

    Free.sjp

    <%@ page language="java" import="java.util.*,cn.test.*" pageEncoding="ISO-8859-1"%>

  • 转-使用LoadRunner测试Oracle数据库经验总结

    2011-09-07 09:35:45

    1、登录SQL*Plus的操作也录制在Action中,添加迭代运行会报错,提示如下:

      Action.c(11): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=24309:

      Action.c(11): Error:     ORA-24309: 已连接至服务器

      Action.c(11): server_attach: ERROR, return-code=LRDE2009.  ServerHandle=OraSrv1, ServerID="customs"

      Abort was called from an action.

      解决办法:1)将登陆SQL*Plus的操作录制在vuser_init中;2)同时录制退出SQL*Plus的操作

      2、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:

      Action.c(37): Error: lrdo_ora8_exec: "OCIStmtExecute" return-code=OCI_ERROR,error-code=00054:

      解决方法:把lrd_ora8_exec(OraSvc1, OraStm<i>, 0, 0, &uliRowsProcessed, 0, 0, 0, 0, 0);

      改为:lrd_ora8_exec(OraSvc1, OraStm<i>, 0, 0, &uliRowsProcessed, 0, 0, 0, 0, 1);

      3、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:

      Action.c(13): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=12514:

      解决办法:开启数据库服务器监听,启动数据库服务器。

      4、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:

      Action.c(26): Error: C interpreter run time error: Action.c (26): Error -- Unresolved symbol : lrd_session_begin.

      解决办法:在vuser_init中添加 #include "lrd.h"

      5、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:

      Starting iteration 1.

      Starting action Action.

      Action.c(13): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=12541:

      Action.c(13): Error: ORA-12541: TNS: 没有监听器

      Action.c(13): server_attach: ERROR, return-code=LRDE2009. ServerHandle=OraSrv1, ServerID="lr"

      解决办法:启动数据库服务器,开启listener。如果需要使用isqlplus的话,开启服务:$isqlplusctl start

      6、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:

      Action.c(13): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=12514:

      Action.c(13): Error: ORA-12514: TNS: 监听进程不能解析在连接描述符中给出的 SERVICE_NAME

      Action.c(13): server_attach: ERROR, return-code=LRDE2009. ServerHandle=OraSrv1, ServerID="lr"

      解决办法:检查压力机Oracle客户端解析文件tnsnames.ora,确保正确配置了这个文件。

      检查测试的数据库服务器,确保正确配置了监听,并已经开启。

       7、使用Controller产生负载,最后测试数据只有以下4条性能曲线:Running Vusers、Trans Response Time、Trans/Sec(Passed)、Total Trans/Sec(Passed)、其余的Throughput、HTTP Responses per Second、Connections等等性能统计项均显示为灰色,没有统计数据

      解决办法:因为sqlplus.exe是一个win32程序,那么在测试的过程中只统计上面的四项,它不会统计Throughput这些Web测试才有的性能曲线。

      所以,如果想对数据库服务器测试的更全面,这个时候就要使用isqlplus来发出查询。

      1)在DB server上启动isqlplus服务:isqlplusctl start

      2)启动VuGen,在客户端(压力机)启动isqlplus的Web页面

      3)输入用户名、密码,我这里采用scott/tiger@lr

      OK!录制完成之后,产生压力执行测试。顺利结束之后,可以看到如下曲线已经抓取出来了:

      Throughput、HTTP Responses per Second、Connections

      剩余的工作就是如何分析性能数据了。

  • 转-LR 分析内存泄露

    2011-08-27 11:35:30

     服务响应的时间标准

     

    参考了业内比较通行的2-5-10原则”——当然你也可以为自己的测试制定其他标准,只要得到企业内的承认就可以。所谓的“2-5-10原则,简单说,就是当用户能够在2秒以内得到响应时,会感觉系统的响应很快;当用户在2-5秒之间得到响应时,会感觉系统的响应速度还可以;当用户在5-10秒以内得到响应时,会感觉系统的响应速度很慢,但是还可以接受;而当用户在超过10秒后仍然无法得到响应时,会感觉系统糟透了,或者认为系统已经失去响应,而选择离开这个Web站点,或者发起第二次请求。

     

    针对基础数据库添加企业信息:

    添加10家企业,9家成功,1家失败,失败详细信息

    Action.c(62): Error -26612: HTTP Status-Code=500 (Internal Server Error) for

    "http://202.117.99.211/basedatabasesite/PSInfo/IndustryFact/PSBaseInfoAdd.aspx?

    PSClassCode=1&%3f"

     

    Monitor name :Windows Resources. Cannot access data for measurement Processor|% Processor Time|_Total on machine 202.117.99.211.

    Details:检测出一个含有负分母值的计数器。

     

    Hint:

    Check that there is such a measurement on the machine (use the Add Machine dialog box) (entry point: CNtMeasurement::GetNewData3).[MsgId: MMSG-47295]

     

    功能名称:企业基本信息维护,添加企业基本信息

    10用户模拟并发操作

    系统响应时间:最短1.078最长4.901秒,属于可接受范围

    资源使用情况:

    内存分析

    其中:

    Handle Count(process _total)值由71030变化为71515差值485bytes

    private bytes值由2442407936变化为2469638144差值27230208bytes变化范围约3M

    committed bytes值由2625691648变化为2652794880差值27103232

     

    可用内存变化情况                                                              虚拟内存变化

    由以上2表可以看出,在系统20运行20秒时候存在较多的页交换

    此时查看error发现

    虚拟用户信息出错时间为第20

    http返回值500表示内部服务器错误。由此发现错误造成了内存泄漏。

     

  • 转-error -27792,-27796等问题

    2011-08-27 11:33:48

    error -27792,-27796等问题,都是和xp系统的tcp连接数有限制导致,默认是10,修改下这个默认值为150或者250,基本上就可以解决了。
    修改tcp连接数,网上有很多方法,我用的是最偷懒的方法,就是装一个bit精灵,里面有“tcp连接数破解补丁”,设置下就好了。

    我就是这么干的,貌似没有啥问题了。
  • 转-响应时间和吞吐量

    2011-08-27 11:28:43

    计算机系统的总体性能标准是响应时间和吞吐量。

    响应时间是提交请求和返回该请求的响应之间使用的时间。示例包括:

    • 数据库查询花费的时间
    • 将字符回显到终端上花费的时间
    • 访问 Web 页面花费的时间

    吞吐量是对单位时间内完成的工作量的量度。示例包括:

    • 每分钟的数据库事务
    • 每秒传送的文件千字节数
    • 每秒读或写的文件千字节数
    • 每分钟的 Web 服务器命中数

    这些度量之间的关系很复杂。有时可能以响应时间为代价而得到较高的吞吐量,而有时候又要以吞吐量为代价得到较好的响应时间。在其他情况下,一个单独的更改可能对两者都有提高。可接受的性能基于合理的吞吐量与合理的响应时间相结合。"

    ----摘抄于 IBM-性能目标

    http://publib.boulder.ibm.com/infocenter/systems/index.jsp?topic=/com.ibm.aix.prftungd/doc/prftungd/corr_svmon_ps_outputs.htm

    悟:曾经在实际的测试工作中遇到这种情况,某一个应用程序,用LR进行性能测试,项目组曾经把思考时间分别设为10秒和0秒,得到了两个差别较大的并发数,曾经有位资深人士说,如果思考时间够大,即使用很差的机器也能做出很大的并发数.

    在性能测试中,作为评价性能好坏的两个重要指标:吞吐量和响应时间, 是很容易让人混淆的。
    吞吐量:字面上的意思是单位时间里处理任务的能力,它的单位常常以hits/sec或者MB/sec为主,它以系统资源为对象的,因此系统性能的好坏直接影响了吞吐量的(理论)极限值。

    响应时间:这里是指从发送请求到完成响应的整个过程所经历的时间,它的单位常常以s或者ms为主。它是以某个请求为对象的,因此请求的大小以及复杂程度直接影响到响应时间的长短。

    这里提到一个概念是“排队论”(http://en.wikipedia.org/wiki/Queuing_theory), 在计算机系统中,这个概念是最常见的,了解排队理论对于我们理解吞吐量和响应时间的关系以及区别很有帮助!
    通常,平均响应时间越短,系统吞吐量越大;平均响应时间越长,系统吞吐量越小;
    但是,系统吞吐量越大, 未必平均响应时间越短;因为在某些情况(例如,不增加任何硬件配置)吞吐量的增大,有时会把平均响应时间作为牺牲,来换取一段时间处理更多的请求。
    一个例子,比如一个理发店,原先只有一个理发师,因为穷,只买的一张理发椅子,和一个长凳用来方便等待的人休息。理发师一次只能处理一个客户,其他等待的用户显得很不耐烦,外面打算进来理发的人也放弃了这家店理发的打算。。。
    有一天,理发师有钱了,他多买了2个理发椅子,这样,他可以同时给3个人理发,当其中一个人理到一定阶段需要调整或者定型的时候,他就转到另外一个客户去 修剪头发,依次类推,这样,他发现一天他可以理的人数比以前增多了,但是还会有一些后来的客户抱怨等待时间太长。
    后来,理发师打算招聘2名学徒帮助他一起干活,这样,他发现每天的理发效率增加了将近2倍,而且客户的等待时间明显也减少了许多。但是成本增多了,理发用具,洗发水,发工资,这让他觉得开个理发店也要精打细算:)
  • 转-LoadRunner中的web_submit_data和web_submit_form

    2011-08-27 11:23:23

    在LoadRunner中有两个常用函数:Web_submit_form和Web_submit_data,在群里有人问这两个函数有什么区别。为什么会有两个不同却功能相似的函数。区别在哪里。

    首先,从工具的角度来说,厂商推荐使用Web_submit_form函数,因为这个函数看起来更易用,需要关注的东西较少。但是,从个人的角度来 说,我推荐使用Web_submit_data函数。因为这个函数提供更多技术细节。在测试的过程中可控性更高。下面我就详细解释一下两个函数的工作机 制:

    首先看一下下面这段HTML代码

    Html代码
    1. <Form Action=login.asp Method=“POST”>  
    2. <input name=user value=“”>  
    3. <input name=password value=“”>  
    4. <input type=hidden name=sessionID value=15379>  
    5. </Form>  

     

     

    在录制的过程中,当打开这个页面的时候,这个页面会放在LR的Cache中,之后当我们输入了用户名、口令之后,点了一下提交。Browser会向LR Record Proxy发送一个提交请求,提交内容应该是这样的:

    Loadrunner代码
    1. POST login.asp HTTP/1.1  
    2. user=steve password=buba  
    3. sessionID=15379  

     

     

    这时候,LR会自动比较提交的内容和Cache的内容,首先它会比较提交的数据项和Cache中的数据项是否一致。页面中有三个输入域user、 password、sessionID,而提交的内容也有这三项数据,所以它认为提交数据使用了Cache中的页面,之后它会继续比较具体数据的值。它会 发现sessionID的值和Cache中的值是一样的。但是user和password的值不一样。

    这时候,Web_submit_form和Web_submit_data的区别就出现了:

    Loadrunner代码
    1. web_submit_form(”start",  
    2. ITEMDATA,  
    3. "name=user""value=steve", ENDITEM,  
    4. "name=password""value=bean", ENDITEM,  
    5. LAST);  

     

     

     

    Loadrunner代码
    1. web_submit_data(”start",  
    2. “Action=login.asp”,  
    3. “Method=POST”  
    4. ITEMDATA,  
    5. "name=user""value=steve", ENDITEM,  
    6. "name=password""value=bean", ENDITEM,  
    7. "name=sessionID""value=15379", ENDITEM,  
    8. LAST);  

     

     

    可以看到,Web_submit_form只提供了和Cache中有差别的数据,其余的数据会自动从Cache中取。而Web_submit_data则提供了所有的数据,不管Cache存在不存在Web_submit_data都是可以工作的。

    所以厂商会推荐使用Web_submit_form,因为它看起来更易用,甚至关联都不需要作,就能直接回放。但是这种情况只能对于简单系统适用。 对于一些银行或者移动的复杂系统来说,有时侯会对Cache作特殊操作,Web_submit_form有时侯就会报一些莫名其妙的错出来。而 Web_submit_data则跟Cache内容无关。稳定性和可控性都要比Web_submit_form要高很多。所以个人推荐尽量使用 Web_submit_data函数。  

        但是之前遇到过一个录制上传文件的脚本问题起初用了web_submit_data提交表单怎么都不成功(脚本回访通过,但是时间文件没有上传),改用web_submit_form后直接就成功了,到现在还没有分析出具体原因,慢慢学习吧!

  • 转-几个免费网站监测工具---有利于性能测试

    2011-08-27 11:22:21

    我租的服务器前段时间每隔一段时间总要挂一挂,所以每次出门的时候心里总是七上八下的,总挂念着服务器别在我出去的时候给挂了。于是时不时拿手机出来上一下看看是不是出问题了。累,且不及时,如果没有GPRS包月的话,那个流量费也比较可观的。

    于是我产生个想法:找个网站监测工具,然后配个移动的邮箱,这样监测发现问题后会发信给移动邮箱,然后移动通知。虽然139邮箱是否有这功能不知道,可是应该好像有的邮箱有类似功能。

    找到一些不错的网站监测工具,如果你也有类似需求,不妨试试:

    Site24×7– 这算是一个老牌的统计了,免费注册后可以体验半个月标准帐户,可以监测网站访问、DNS服务器、邮箱、网页分析等。15天后会降为免费帐户,仅能监测网站运行状态,且仅能监测2个网站、60分钟监测一次。出现问题会发电邮通知你。

    mon.itor.us– 免费工具,可以监控HTTP、HTTPS、FTP、SIP、TCP、UDP、IMAP、SMTP、POP3、PING、DNS等,你的网站出现问题会发信通知你。另外还可以在你的网页中加入统计代码,做为一个统计工具。

    SiteUptime– SiteUptime为站长们提供网站运行状态监测服务,免费用户可以监测一个网站,30或60分钟检查一次,可监测80(http)、21(ftp)、 25(smtp)、110(pop3)这4个端口,提供4台监测服务器供你选择(旧金山、芝加哥、纽约、伦敦),网站不能访问和恢复访问的时候会发送 Email通知你。

    WebPerform– WebPerform有中文版的,免费服务可监测一个网站,可选3个监测节点,当网站出现访问速度过慢或无法访问会发 Email给你警报。WebPerform自定义功能强大,你可以在查看你的网站24小时中的访问速度统计表,自己自定义发送Email警报的情况。

    有这几个工具,这样就可以放心外出了:)

  • 转-LR瓶颈

    2011-08-27 11:21:15

    下面我们来看一下

    首先我们要监视

    %processor time(processor_total):

    受的最大上限是

    %User time(processor_total):

    等。如果该值很高,可考虑增加索引,尽量使用简单的表联接,水平分割大表格等方法来降

    低该值。

    %DPC time(processor_total):

    Processor:% Processor Time

    %Disk time(physicaldisk_total)

    的百分比。如果三个计数器都比较大,那么硬盘不是瓶颈。如果只有

    外两个都比较适中,硬盘可能会是瓶颈。在记录该计数器之前,请在

    行窗口中运行

    Availiable bytes(memory)

    则说明计算机上总的内存可能不足,或某程序没有释放内存。

    Context switch/sec(system): (

    的大小,你应该监视这三个计数器(包括上面的一个)。增加线程数可能会增加上下文切换

    次数,这样性能不会上升反而会下降。如果十个实例的上下文切换值非常高,就应该减小线

    程字节池的大小。

    %Disk reads/sec(physicaldisk_total):

    %Disk write/sec(physicaldisk_total):

    Page faults/sec:

    的影响。

    Pages per second:

    Working set:

    器有足够的空闲内存,页就会被留在工作集中,当自由内存少于一个特定的阈值时,页就会

    被清除出工作集。

    Avg.disk queue length:

    超过磁盘数的

    Average disk read/write queue length:

    Disk reads/(writes)/s:

    量。

    Average disk sec/read:

    Average disk sec/transfer:

    Bytes total/sec:

    可以用该计数器的值和目前网络的带宽比较

    Page read/sec:

    理页读取总数。由于物理

    更高效的查询或者改变数据库设计等方法,使开销减到最小。

    Page write/sec:

    1.

    如果系统由于应用程序代码效率低下或者系统结构设计有缺陷而导致大量的上下文切换

    (context switches/sec

    吞吐量降低并且

    下文切换次数过高

    从图的整体看

    switches/sec

    2.

    如果_______并且处理器的利用率%Processor

    time

    瓶颈

    %processor time

    经不能满足程序需要

    3.

    内存问题主要检查应用程序是否存在内存泄漏

    数器和

    应该通过一个长时间的

    图中可以看到该程序并不存在内存泄露的问题

    时候

  • 转-实际测试LR遇到问题27796 fail to connect to server 10060

    2011-08-27 11:16:48

    曾经遇到过一个问题,在一次性能测试过程中,使用http协议的多用户向服务器发送请求。设置了持续时间,出现错误 为:vuser_init.c(20): 错误 -27796: 连接服务器“localhost”失败: [10060] Connection timed out

    因为负载生成器的性能太好,发数据包特别快,服务器也响应特别快,从而导致负载生成器的机器的端口在没有 timeout之前就全部占满了。在全部占满后,就会出现上面的错误。执行netstat –na命令,可以看到打开了很多端口。所以就调整TCP的time out。即在最后一个端口还没有用到时,前面已经有端口在释放了。

    [b]官方的troubleshooting:[/b]
    查看工具的troubleshooting,如下:[code]Message Code 27796
    Failed to connect to server 'hostname';port_ld': 'reason'.
    Unable to connect to the specified server and port.
    Troubleshooting
    o      Try to address the reason provided for the connection failure.
    o      Try to access the application with a browser from the injector machine and from another machine (such as the recording machine).
    o      Check that you accurately specified the correct host name and port.
    o      Ping the host/port.
    o      Check if the server application you are trying to access is running.
    o      If you used a hostname, check if it was resolved to the correct address.
    o      Check if the server application is listening to the right port. [/code]均不是解决之道。

    [b]成功的解决方法:[/b]

    在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters里,有如下两个键值:
    TcpTimedWaitDelay
    MaxUserPort
    1,这里的TcpTimedWaitDelay默认值应该中是30s,所以这里,把这个值调小为5s(按需要调整)。
    2,也可以把MaxUserPort调大(如果这个值不是最大值的话)。

    反复验证,问题解决。

    TcpTimedWaitDelay 值決定了 TCP/IP 必須經過多久,才能釋出已關閉的連線及重複使用它的資源。這個關閉和釋出的間隔稱為 TIME_WAIT 狀態,或是區段生命期限上限 (2MSL) 狀態的兩倍。在這段時間內,通往用戶端和伺服器的連線重新開啟的成本,比建立新的連線低。藉由縮減這個項目的值,TCP/IP 可以更快釋出已關閉的連線,提供更多資源給新的連線。如果執行中的應用程式需要快速釋出、建立新連線,或多個連線在 TIME_WAIT 狀態中造成通訊量太低,因而需要進行調整的話,請調整這個參數。

    預設值是 0xF0,它會將等待時間設為 240 秒(4 分鐘)。

    最小的建議值是 0x1E,它會將等待時間設為 30 秒。請利用這個程序來檢視或自訂您的值。

    啟 動 regedit 指令,瀏覽至 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters 登錄次機碼,建立名稱為 TcpTimedWaitDelay 的新 REG_DWORD 值。
    將值設為十進位 30,也就是十六進位的 0x0000001e。這個值會將等待時間設為 30 秒。
    關閉登錄編輯器。
    停止並重新啟動系統。
    MaxUserPort
    MaxUserPort 值決定了當應用程式向系統要求可用的使用者埠時,TCP/IP 所能指派的最高埠號。如果您的系統報告建立 Socket 時,發生錯誤異常狀況,可能是匿名(短期)埠的數量不當所造成,當系統開啟大量的埠來建立 Web 服務、資料庫或其他遠端資源的連線時,尤其如此。

    或者:

    这事连接超时的意思,表示向服务器发出请求在规定时间内没有收到响应。是一个正常的报错,跟注册表没有关系。
    这是在runtime setting里能够设置的。在preferences里的options里有三项关于网络延时限制的设置值可以自行根据需求修改。
  • 转-使用LoadRunner 编写JAVA 测试脚本

    2011-08-27 11:14:02

    使用LoadRunner 编写JAVA 测试脚本

    发布: 2008-11-14 10:39 | 作者: chenxin9821 | 来源: 思步网

    以前在使用loadrunner 的过程中,基本很少关注过手动编写测试脚本,这也与手头工作有一定的关系,平时难以接触到这方面的测试类型。虽然这期间也看过,但是感觉当时的 Loadrunner对JAVA语言脚本的支持较弱,JDK还是1.1、1.2的版本,显然太不合时宜了,于是也就放弃了。     昨晚,在网上看了看Loadrunner8.1版本,已经支持到了JDK1.5版本,结合目前手头测试工作,觉得有必要细看一下。 要使得Loadrunner8.1版本能够支持JAVA语言脚本,以下几点需要注意: 1.JDK的版本必须是5.0(我机器上原先装的是JDK6.0,折腾了好长一段时间,总是报 ERROR loading javai.dll的错,最后没办法只能装5.0 ,Loadrunner对jdk的支持有点弱智)。设置好java 环境。 2.Loadrunner 要支持JAVA语言必须,安装一个补丁。( java_protocol_fixes_Feb_05 可以网上找一下,这儿没法上传)  打开virtual user generator ,新建java vuser,看一下run-time settings,java environment setting 设置 如下图  打开 java vuser的编辑界面 import lrapi.lr; public class Actions
    {     public int init() {
            return 0;
        }//end of init     public int action() {
            return 0;
        }//end of action     public int end() {
            return 0;
        }//end of end
    } 可以看到标准的java class 写法。 Actions 类包含三个方法:initactionend。 init       登录到服务器    Vuser 已初始化(已加载) action  客户端活动        Vuser 处于“正在运行”状态 end       注销过程          Vuser 完成或停止        这时候完全可以理解loadrunner如何执行java 测试脚本代码了,在Actions 类中调用测试java类,然后在脚本中需要的地方加上loadrunner自带的一些函数方法等(比如事务函数、命令行分析函数、信息性函数、运行时函 数),当然还能进行一些参数化的设置,最后在control上增加vuser数量(也就是增加线程)。        如果抛开Loadrunner,使用一些开源的unit工具,完全也能够实现loadrunner这些功能,只不过可能在监控方面,没有 loadrunner做的那么精致罢了。 做个小例子。
    • 写个测试java类
    public class  testjava
    {
        public static void main(String args[])
        {
            System.out.println("hello world");
        }
        public String getStr()
        {
            return "hello world";
        }
        public String getStr(String str)
        {
            return str;
        }
    }
    • 通过java Vuser调用测试java类
    /*
    * LoadRunner Java script. (Build: 754)
    *
    * Script. Description:
    *                     
    */ import lrapi.lr;
    import org.apache.log4j.*; public class Actions
    {
        static Logger logger = Logger.getLogger(Actions.class.getName());
        int i=10;     public int init() { //读取log4j属性文件
            PropertyConfigurator.configure("D:\\MyEclipse Enterprise Workbench 5.1.0 GA\\eclipse\\workspace\\testjava\\bin\\log4j.properties");
            return 0;
        }     public int action() {        lr.start_transaction("事务");
           testjava t=new testjava();//调用测试类
           lr.message( "获取返回值:"+   t.getStr("<NewParam>")); //NewParam是loadrunner参数化设置的参数            //设置集合点
               lr.rendezvous("集合点");
               //设置等待时间
               lr.think_time(2);
               lr.end_transaction("事务",lr.PASS);
               //检索运行 Vuser 的计算机的名称
               String my_host = lr.get_host_name( );
           lr.message( "计算机的名:"+  my_host+"  "+i);
               lr.log_message (lr.get_vuser_id() +" "+ t.getStr("<NewParam>"));        //lr.get_vuser_id  返回当前 Vuser 的 ID
           logger.info(lr.get_vuser_id() +" "+ t.getStr("<NewParam>"));
               lr.enable_redirection(true);            System.out.println("This is an informatory message…"); // 已重定向
              // System.err.println("This is an error message…"); // 已重定向            lr.enable_redirection(false);
               System.out.println("This is an informatory message…"); // 未重定向
              // System.err.println("This is an error message…"); // 未重定向         return 0;
        }     public int end() {
            return 0;
        }
    }
    • Vuser运行 log
    Virtual User Script. started
    Starting action vuser_init.
    Ending action vuser_init.
    Running Vuser...
    Starting iteration 1.
    Starting action Actions.
    Notify: Transaction "事务" started.
    获取返回值:1
    Rendezvous 集合点
    Notify: Transaction "事务" ended with "Pass" status (Duration: 0.0175).
    计算机的名:ms-599008241fcd  10
    -1 1
    System.out:  INFO (Actions.java:37) - -1 1
    Notify:
    System.out: This is an informatory message…                                                                                                                                                      Notify:
    Ending action Actions.
    Ending iteration 1.
    Ending Vuser...
    Starting action vuser_end.
    Ending action vuser_end.
    Vuser Terminated.
    • Control 运行 截图
    看到定义的“事务”。
    • log4j属性文件
    log4j.rootCategory=DEBUG, stdout,R
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p (%F:%L) - %m%n
    log4j.appender.R=org.apache.log4j.RollingFileAppender
    log4j.appender.R.File=c:/log.txt
    log4j.appender.R.MaxFileSize=100KB
    log4j.appender.R.MaxBackupIndex=1
    log4j.appender.R.layout=org.apache.log4j.PatternLayout
    log4j.appender.R.layout.ConversionPattern=%d{yyyy MMM dd HH:mm:ss} %-5p %c - %m%n
    • 20个Vuser运行一次,查看一下log文件,可以看一下参数设置的效果
    2008 三月 02 18:06:43 INFO  Actions - 7 91
    2008 三月 02 18:06:43 INFO  Actions - 16 101
    2008 三月 02 18:06:43 INFO  Actions - 17 121
    2008 三月 02 18:06:43 INFO  Actions - 13 41
    2008 三月 02 18:06:43 INFO  Actions - 10 181
    2008 三月 02 18:06:43 INFO  Actions - 18 141
    2008 三月 02 18:06:43 INFO  Actions - 19 151
    2008 三月 02 18:06:43 INFO  Actions - 6 71
    2008 三月 02 18:06:43 INFO  Actions - 11 1
    2008 三月 02 18:06:43 INFO  Actions - 9 131
    2008 三月 02 18:06:43 INFO  Actions - 2 191
    2008 三月 02 18:06:43 INFO  Actions - 12 21
    2008 三月 02 18:06:43 INFO  Actions - 5 51
    2008 三月 02 18:06:43 INFO  Actions - 15 81
    2008 三月 02 18:06:43 INFO  Actions - 20 161
    2008 三月 02 18:06:43 INFO  Actions - 3 11
    2008 三月 02 18:06:43 INFO  Actions - 1 171
    2008 三月 02 18:06:43 INFO  Actions - 8 111
    2008 三月 02 18:06:43 INFO  Actions - 14 61
    2008 三月 02 18:06:43 INFO  Actions - 4 31
    loadrunner 提供的.NET JAVA等语言脚本支持,为测试的灵活性、多样性提供了极大的方便。
  • web性能测试基本性能指标

    2011-08-14 12:51:23

    Web性能测试的部分概况一般来说,一个Web请求的处理包括以下步骤:

    (1)客户发送请求

    (2)web server 接受到请求,进行处理;

    (3)web server 向DB获取数据;

    (4)web server生成用户的object(页面),返回给用户。给客户发送请求开始到最后一个字节的时间称为响应时间(第三步不包括在每次请求处理中)。

    1.事务(Transaction

    web性能测试中,一个事务表示一个从用户发送请求->web server接受到请求,进行处理-> web server DB获取数据->生成用户的object(页面),返回给用户的过程,一般的响应时间都是针对事务而言的。

    2.请求响应时间

    请求响应时间指的是从客户端发起的一个请求开始,到客户端接收到从服务器端返回的响应结束,这个过程所耗费的时间,在某些工具中,响应通常会称为“TTLB”,即"time to last byte",意思是从发起一个请求开始,到客户端接收到最后一个字节的响应所耗费的时间,响应时间的单位一般为或者毫秒。一个公式可以表示:响应时间=网络响应时间+应用程序响应时间。标准可参考国外的3/5/10原则:

    1)在3秒钟之内,页面给予用户响应并有所显示,可认为是“很不错的”;

    2)在3~5秒钟内,页面给予用户响应并有所显示,可认为是“好的”;

    3)在5~10秒钟内,页面给予用户响应并有所显示,可认为是“勉强接受的”;

    4)超过10秒就让人有点不耐烦了,用户很可能不会继续等待下去;

    3、事务响应时间

       事务可能由一系列请求组成,事务的响应时间主要是针对用户而言,属于宏观上的概念,是为了向用户说明业务响应时间而提出的.例如:跨行取款事务的响应时间就是由一系列的请求组成的.事务响应时间是直接衡量系统性能的参数.

    4.并发用户数

    并发一般分为2种情况。一种是严格意义上的并发,即所有的用户在同一时刻做同一件事情或者操作,这种操作一般指做同一类型的业务。比如在信用卡审批业务中,一定数目的拥护在同一时刻对已经完成的审批业务进行提交;还有一种特例,即所有用户进行完全一样的 操作,例如在信用卡审批业务中,所有的用户可以一起申请业务,或者修改同一条记录。

      另外一种并发是广义范围的并发。这种并发与前一种并发的区别是,尽管多个用户对系统发出了请求或者进行了操作,但是这些请求或者操作可以是相同的,也可以是不同的。对整个系统而言,仍然是有很多用户同时对系统进行操作,因此也属于并发的范畴。

      可以看出,后一种并发是包含前一种并发的。而且后一种并发更接近用户的实际使用情况,因此对于大多数的系统,只有数量很少的用户进行严格意义上的并发。对于WEB性能测试而言,这2种并发情况一般都需要进行测试,通常做法是先进行严格意义上的并发测试。严格意义上的用户并发一般发生在使用比较频繁的模块中,尽管发生的概率不是很大,但是一旦发生性能问题,后果很可能是致命的。严格意义上的并发测试往往和功能测试关联起来,因为并发功能遇到异常通常都是程序问题,这种测试也是健壮性和稳定性测试的一部分。

    用户并发数量:关于用户并发的数量,有2种常见的错误观点。 一种错误观点是把并发用户数量理解为使用系统的全部用户的数量,理由是这些用户可能同时使用系统;还有一种比较接近正确的观点是把在线用户数量理解为并发用户数量。实际上在线用户也不一定会和其他用户发生并发,例如正在浏览网页的用户,对服务器没有任何影响,但是,在线用户数量是计算并发用户数量的主要依据之一。

    5.吞吐量

    指的是在一次性能测试过程中网络上传输的数据量的总和.吞吐量/传输时间,就是吞吐率.

    6、 TPStransaction per second

    每秒钟系统能够处理的交易或者事务的数量.它是衡量系统处理能力的重要指标.

    7、点击率

    每秒钟用户向WEB服务器提 交的HTTP请求数.这个指标是WEB应用特有的一个指标:WEB应用是"请求-响应"模式,用户发出一次申请,服务器就要处理一次,所以点击是WEB应用能够处理的交易的最小单位.如果把每次点击定义为一个交易,点击率和TPS就是一个概念.容易看出,点击率越大,对服务器的压力越大.点击率只是一个性能参考指标,重要的是分析点击时产生的影响。需要注意的是,这里的点击并非指鼠标的一次单击操作,因为在一次单击操作中,客户端可能向服务器发出多个 HTTP请求.

    8.资源利用率

    指的是对不同的系统资源的使用程度,例如服务器的CPU利用率,磁盘利用率等.资源利用率是分析系统性能指标进而改善性能的主要依据,因此是WEB性能测试工作的重点.

    资源利用率主要针对WEB服务器,操作系统,数据库服务器,网络等,是测试和分析瓶颈的主要参考.WEB性能测试中,更根据需要采集相应的参数进行分析。

    通用指标(指Web应用服务器、数据库服务器必需测试项)

    ?-g4piw*BF&@T128935

    指标51Testing软件测试网r!HA0HL|

    说明51Testing软件测试网H}1iT:j4P T

    ProcessorTime服务器CPU占用率,一般平均达到70%时,服务就接近饱和
    Memory Available Mbyte可用内存数,如果测试时发现内存有变化情况也要注意,如果是内存泄露则比较严重
    Physicsdisk Time物理磁盘读写时间情况

    lr@!GWAz|128935Web服务器指标51Testing软件测试网BHQBB'Q Sn

    指标

    'F-mOY1^128935

    说明

    +vH/Oa,_aByCW }128935
    Requests Per Second(Avg Rps)平均每秒钟响应次数=总请求时间 / 秒数
    Avg time to last byte per terstion (mstes)平均每秒业务脚本的迭代次数 ,有人会把上面那个混淆
    Successful Rounds成功的请求
    Failed Requests失败的请求
    Successful Hits成功的点击次数
    Failed Hits失败的点击次数
    Hits Per Second每秒点击次数
    Successful Hits Per Second每秒成功的点击次数
    Failed Hits Per Second每秒失败的点击次数
    Attempted Connections尝试链接数

    a0C9rn1X)Q128935数据库服务器性能指标

    RSPu9Z(O_(rn9|!w128935

    指标51Testing软件测试网K8],Ng.|9KmI

    说明51Testing软件测试网%S.?rR"^.j-R.a\

    User 0 Connections用户连接数,也就是数据库的连接数量
    Number of deadlocks数据库死锁
    Butter Cache hit数据库Cache的命中情况

    ^$]j(`6T Ro128935系统的瓶颈定义

    hf#k$t'g128935

    性能项

    m*pU5\M'c;S;|5[128935

    命令51Testing软件测试网$](jc{,\@ {|

    指标51Testing软件测试网 ^No5v4^v%@:D`

    CPU限制vmstat当%user+%sys超过80%时
    磁盘I/O限制Vmstat当%iowait超过40%(AIX4.3.3或更高版本)时
    应用磁盘限制Iostat当%tm_act超过70%时
    虚存空间少Lsps,-a当分页空间的活动率超过70%时
    换页限制Iostat, stat虚存逻辑卷%tm_act超过I/O(iostat)的30%,激活的虚存率超过CPU数量(vmstat)的10倍时
    系统失效Vmstat, sar页交换增大、CPU等待并运行队列
    51Testing软件测试网.k,g,_eo0wP

      稳定系统的资源状态51Testing软件测试网L(dGx;rQ\(B%S{ ji

    51Testing软件测试网X$Ya1Pa a+n w@f#p

    性能项51Testing软件测试网 ^ R zH0hS[O|.h

    资源51Testing软件测试网@vcLW| Yi

    评价51Testing软件测试网i5G U+t#L%g

    CPU占用率70%
    85%
    90%+很差
    磁盘I/0<30%
    <40%
    <50%+很差
    网络<30%带宽
    运行队列<2*CPU数量
    内存没有页交换
    每个CPU每秒10个页交换
    更多的页交换很差
    Y$~&pu?(z3vC128935

    /U1p+Q;w#Y2YyN(wa,\128935  通俗理解:

    3F*g{4n3KC128935

    4Y(Q}}5]U x+{ g128935  日访问量51Testing软件测试网@1\5z#`h*J.{,T1p

    51Testing软件测试网8HFx"l!fn5n1zL@+`A

      常用页面最大并发数51Testing软件测试网5^V4{M%[{

    51Testing软件测试网 l e YF?} BrvO

      同时在线人数51Testing软件测试网[4U3FoaU7P f#h!B

    51Testing软件测试网{j1AZb

      访问相应时间

    0]8vXc|K12893551Testing软件测试网8}V;L1b5Kl

      案例:51Testing软件测试网2sG+y*~yTe

    51Testing软件测试网-c7ua$bl,Ch

      最近公司一个项目,是个门户网站,需要做性能测试,根据项目特点定出了主要测试项和测试方案:

    K] X.g6S4~12893551Testing软件测试网V9?"i.s,Wv

      一种是测试几个常用页面能接受的最大并发数(用户名参数化,设置集合点策略)

    G;E3v E S12893551Testing软件测试网#Q,G X$Vj,n)F

      一种是测试服务器长时间压力下,用户能否正常操作(用户名参数化,迭代运行脚本)

    *t]nf a'e/e12893551Testing软件测试网%j ~ s+@1iN

       一种则需要测试服务器能否接受10万用户同时在线操作,如果是用IIS做应用服务器的话,单台可承受的最大并发数不可能达到10万级,那就必须要使用集 群,通过多台机器做负载均衡来实现;如果是用websphere之类的应用服务器的话,单台可承受的最大并发数可以达到10万级,但为性能考虑还是必须要 使用集群,通过多台机器做负载均衡来实现;通常有1个简单的计算方式,1个连接产生1个session,每个session在服务器上有个内存空间大小的 设置,在NT上是3M,那么10万并发就需要300G内存,当然实际使用中考虑其他程序也占用内存,所以准备的内存数量要求比这个还要多一些。还有10万个用户同时在线,跟10万个并发数是完全不同的2个概念。这个楼上已经说了。但如何做这个转换将10万个同时在线用户转换成多少个并发数呢?这就必须要有大量的历史日志信息来支撑了。系统日志需要有同时在线用户数量的日志信息,还需要有用户操作次数的日志信息,这2个数据的比例就是你同时在线用户转换到并发数的比例。另外根据经验统计,对于1个JAVA开发的WEB系 统(别的我没统计过,给不出数据),一般1台双CPU、2G内存的服务器上可支持的最大并发数不超过500个(这个状态下大部分操作都是超时报错而且服务 器很容易宕机,其实没什么实际意义),可正常使用(单步非大数据量操作等待时间不超过20秒)的最大并发数不超过300个。假设你的10万同时在线用户转 换的并发数是9000个,那么你最少需要这样的机器18台,建议不少于30台。当然,你要是买个大型服务器,里面装有200个CPU、256G的内存,千 兆光纤带宽,就算是10万个并发用户,那速度,也绝对是嗖嗖的。51Testing软件测试网DN To!Axc

    51Testing软件测试网;w kx\-IBr

      另外暴寒1下,光设置全部进入运行状态就需要接近6个小时。具体的可以拿1个系统来压一下看看,可能会出现以下情况:

    Y(mD Aj9v_12893551Testing软件测试网*{9A/f&vl{D

      1、服务器宕机;51Testing软件测试网'M J$~*P7hHK

    51Testing软件测试网kcZ6^Gqm

      2、客户端宕机;

    2Z%L,{SpZ@j128935

    Q|&Q~:H(S? zB5]128935  3、从某个时间开始服务器拒绝请求,客户端上显示的全是错误;

    aqcsn:d$]u'o128935

    1b&t+Nh e}!Q5Lp128935  4、勉强测试完成,但网络堵塞或测试结果显示时间非常长。假设客户端和服务器之间百兆带宽,百兆/10000=10K,那每个用户只能得到10K,这个速度接近1个64K的MODEM上网的速度;另外以上分析全都没考虑系统的后台,比如数据库、中间件等。

    O?m~ B%{[128935

    b8c l }Q)y$bn128935  1、服务器方面:上面说的那样的PC SERVER需要50台;51Testing软件测试网#U*Xr3C(`

    ,Wg{f:p;Gp){.r/w!]128935  2、网络方面:按每个用户50K,那至少5根百兆带宽独享,估计仅仅网络延迟就大概是秒一级的;51Testing软件测试网c,L_X"Gy)mO

    a,a fl:eE K4C Q128935  3、如果有数据库,至少是ORACLE,最好是SYSBASE,SQLSERVER是肯定顶不住的。数据库服务器至少需要10台4CPU、16G内存的机器;

    aBz3PA4FR7xX B128935

    U~C2C4m128935  4、如果有CORBA,那至少再准备10台4CPU、16G内存的机器;再加上负载均衡、防火墙、路由器和各种软件等,总之没个1000万的资金投入,肯定搞不定。51Testing软件测试网B"wN#?c

    -cC1qqy$[128935   这样的门户系统,由于有用户权限,所以并不象jackie所说大多是静态页面。但只要是多服务器的集群,那么我们就可以通过1台机器的测试结果来计算多 台机器集群后的负载能力的,最多额外考虑一下负载均衡和路由上的压力,比如带宽、速度、延迟等。但如果都是在1台机器上变化,那我们只能做一些指标上的计 算,可以从这些指标上简单判断一下是否不可行,比如10万并发用户却只有1根百兆带宽,那我们可以计算出每个用户只有1K带宽,这显然是不可行的。但实际 的结果还是需要测试了才知道,毕竟系统压力和用户数量不是线性变化的。

    0f5O-v!M X rr12893551Testing软件测试网j)t:eT!{D#c3X q

       这一类系统的普遍的成熟的使用,以及很多软件在方案设计后就能够大致估算出系统的性能特点,都导致了系统在软件性能方面调优的比例并不大(当然不完全排 除后期针对某些代码和配置进行优化后性能的进一步提高),更多的都是从硬件方面来考虑,比如增加内存、硬盘做RAID、增加带宽、甚至增加机器等。

    1d"e^V$]!b Je U/D12893551Testing软件测试网]I4IRYzy/[/Z

      网络技术中的10M 带宽指的是以位计算, 就是 10M bit /秒 ,而下载时的速度看到的是以字节(Byte)计算的,所以10M带宽换算成字节理论上最快下载速度为: 1.25 M Byte/秒!

  • 转-LoadRunner关于验证码的解决方案

    2011-07-07 20:18:23

    现在好多网站系统为了防范,恶意访问系统,在登陆口进行限制,使用验证码登陆。

      验证码是随机产生的,并且验证码在页面上显示为图片。此时想通过loadrunner直接获取服务器发送过来的参数,肯定是不可行的。

      在进行性能测试的时候,有两种办法进行此类系统的测试。

      1、将验证码暂时屏蔽,待完成性能测试后,在恢复。验证码屏蔽一定不会给性能测试带来影响,这是肯定的。

      2、如果要测试系统是在用的系统,屏蔽验证码会带来不安全因素,不能屏蔽验证码。遇到这个问题当然也有办法解决--添加一个页面将验证码的输出到页面,然后用loadrunner获取到。

      JSP为例:

      验证码页面(a.jsp

    <%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
    <%!
    Color getRandColor(int fc,int bc)
    {//给定范围获得随机颜色
        Random random = new Random();
        if(fc>255) fc=255;
        if(bc>255) bc=255;
        int r=fc+random.nextInt(bc-fc);
        int g=fc+random.nextInt(bc-fc);
        int b=fc+random.nextInt(bc-fc);
        return new Color(r,g,b);
    }
    %>
    <%
    //设置页面不缓存
    response.setHeader("Pragma","No-cache");
    response.setHeader("Cache-Control","no-cache");
    response.setDateHeader("Expires", 0);

    // 在内存中创建图象
    int width=60, height=20;
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

    // 获取图形上下文
    Graphics g = image.getGraphics();

    //生成随机类
    Random random = new Random();

    // 设定背景色
    g.setColor(getRandColor(200,250));
    g.fillRect(0, 0, width, height);

    //设定字体
    g.setFont(new Font("Times New Roman",Font.PLAIN,18));

    //画边框
    //g.setColor(new Color());
    //g.drawRect(0,0,width-1,height-1);

    // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
    g.setColor(getRandColor(160,200));
    for (int i=0;i<300;i++)
    {
    int x = random.nextInt(width);
    int y = random.nextInt(height);
    int xl = random.nextInt(12);
    int yl = random.nextInt(12);
    g.drawLine(x,y,x+xl,y+yl);
    }

    // 取随机产生的认证码(4位数字)
    String sRand="";
    for (int i=0;i<4;i++){
    String rand=String.valueOf(random.nextInt(10));
    sRand+=rand;
    // 将认证码显示到图象中
    g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));//调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
    g.drawString(rand,13*i+6,16);
    }

    // 将认证码存入SESSION
    session.setAttribute("rand",sRand);
    System.out.println("---:"+sRand); //sRand就是随机数 验证码 ,把它放到session中,然后合成到图片中
    // 图象生效
    g.dispose();

    //out.clear();
    request.setAttribute("gzip", "false");

    // 输出图象到页面
    ImageIO.write(image, "JPEG", response.getOutputStream());


    out.clear();
    out = pageContext.pushBody();

    %>

    将验证码jsp页面作为图片放到b.jsp页面中:

    <a href="c.jsp">c.jsp</a>

    <br>
    <br>
    <img src="a.jsp"/>

    <iframe. src="c.jsp"/>

      在iframe中放入的c.jsp页面就是 获取验证码的页面。

      这儿加了一个c.jsp 页面链接,主要是用在loadrunner录制脚本的时候,a.jsp 和c.jsp在B页面上加载的顺序不是有序的,因而C.JSP可能获取到的验证码为NULL。

      c.jsp

      Q<%=session.getAttribute("rand")%>Q

      在C.JSP页面上 在取得的验证码前后加上两个Q主要为了loadrunner能够捕获到这个验证码做的标记。

    下面我们看一下 loadrunner脚本:

    Action()
    {
        web_set_max_html_param_len("1024");
        web_url("b.jsp",
            "URL=http://10.10.129.99:8080/blank/b.jsp",
            "Resource=0",
            "RecContentType=text/html",
            "Referer=",
            "Snapshot=t1.inf",
            "Mode=HTTP",
            LAST);

        web_concurrent_start(NULL);

        web_url("a.jsp",
            "URL=http://10.10.129.99:8080/blank/a.jsp",
            "Resource=1",
            "RecContentType=image/jpeg",
            "Referer=http://10.10.129.99:8080/blank/b.jsp",
            LAST);
    /*
        web_url("c.jsp",
            "URL=http://10.10.129.99:8080/blank/c.jsp",
            "Resource=0",
            "RecContentType=text/html",
            "Referer=http://10.10.129.99:8080/blank/b.jsp",
            "Snapshot=t2.inf",
            "Mode=HTTP",
            LAST);
    */
        web_concurrent_end(NULL);

    //获取C.JSP页面上的验证码

        web_reg_save_param("aaa",
            "LB/IC=Q",
            "RB/IC=Q",
            "Ord=1",
            "Search=body",
            LAST);

        web_url("c.jsp_2",
            "URL=http://10.10.129.99:8080/blank/c.jsp",
            "Resource=0",
            "RecContentType=text/html",
            "Referer=http://10.10.129.99:8080/blank/b.jsp",
            "Snapshot=t3.inf",
            "Mode=HTTP",
            LAST);

    //打印出来 也可以通过loadrunner log日志设置查看 param
        lr_log_message("-------:%s", lr_eval_string("{aaa}"));
        return 0;
    }

      运行结果

      运行的时候 要把loadrunner的浏览器给关掉,否则lr的浏览器显示一下,相当于也做了一次请求。

  • loadrunner对数据库进行查询和插入的脚本

    2011-03-02 14:42:41

    (1)VUSER_INIT中脚本(连帹数据库)
    #include "lrd.h"

    static LRD_INIT_INFO InitInfo = {LRD_INIT_INFO_EYECAT};

    static LRD_DEFAULT_DB_VERSION DBTypeVersion[] =

    {

    {LRD_DBTYPE_NONE, LRD_DBVERSION_NONE}

    };
    static LRD_CONNECTION * Con1;
    static LRD_CURSOR * Csr1;
    vuser_init()
    {
    lrd_init(&InitInfo, DBTypeVersion);

    lrd_open_connection(&Con1, LRD_DBTYPE_ORACLE, "用户名", "密码", "服務名", "", 0, 0, 0);


    }
    (2)ACTION中的脚本 (进行数据库操作)
    Action()
    {


    lr_start_transaction("Insert");

    lrd_open_cursor(&Csr1, Con1, 0);
    //lrd_open_cursor(&Csr1, Con1, 0);
    lrd_stmt(Csr1,"SQL娪句", -1, 0, 1, 0);

    lrd_exec(Csr1, 0, 0, 0, 0, 0);

    /*

    PrintRow2, 0);

    lrd_close_cursor(&Csr1, 0);
    */

    lrd_commit(0, Con1, 0);


    lr_end_transaction("Insert", LR_AUTO);

    }
    (3)vuser_end中的脚本(释放资源,关闭数据库)
    vuser_end()
    {
    lrd_close_connection(&Con1, 0, 0);

    lrd_end(0);
    return 0;
    }
  • 转-LoadRunner获取数据库中内容并参数化【已验证】

    2011-03-02 13:50:21

    工作上有使用LoadRunner时又需要到数据库中取相应的数据作为参数,经过一番折腾及验证代码如下

    运行成功条件:需安装oracle客户端,且设置了odbc连接,否则会报错illegal character `\0241'

    Action()
    {

        #include "lrun.h"
        #include "vdf.h"
        #include "print.inl"
        #include "lrd.h"
    //code by shiwanli----------参数定义
     unsigned long rownum;
     char Temp[200];
     char Prem_value[200];
    //code by shiwanli----------以下两行为结构声明,在绑定时使用,如果不声明则会报错
    //code by shiwanli----------LRD_VAR_DESC数据结构声明是很重要的,他是用来存储sql结果数据集的结构体
    //code by shiwanli----------第二个参数,设置结果集中最大行数,在lrd_ora8_fetch中的第二个参数如果大于该值,则会报错。
    //code by shiwanli----------第三个参数,设置结果集中每一行的最大长度,如果获得的查询结果比定义的长,运行时就会报错,提示列被截断
    //code by shiwanli----------最后一个参数是查询结果的类型,可以再帮助中的索引输入data types, database,列出的表格中是各种变量类型的名称
    //code by shiwanli----------常用的数据类型有DT_VARCHAR, DT_DECIMAL, DT_DATETIME, DT_SF, DT_SZ, DT_NUMERIC  
     static LRD_VAR_DESC NUM ={LRD_VAR_DESC_EYECAT, 10, 32, LRD_DBTYPE_ORACLE, {1, 1, 0},DT_LONG_VARCHAR};
     static LRD_VAR_DESC NUM1 ={LRD_VAR_DESC_EYECAT, 10, 32, LRD_DBTYPE_ORACLE, {1, 1, 0},DT_LONG_VARCHAR};
     static LRD_VAR_DESC OBJECT_NAME_D1;
     static LRD_INIT_INFO InitInfo = {LRD_INIT_INFO_EYECAT};
     static LRD_DEFAULT_DB_VERSION DBTypeVersion[] = {{LRD_DBTYPE_NONE,LRD_DBVERSION_NONE}};

     static void FAR * OraEnv1;
     static void FAR * OraSvc1;
     static void FAR * OraSrv1;
     static void FAR * OraSes1;
     static void FAR * OraStm1;
     static void FAR * OraDef1;
     static  unsigned long uliFetchedRows;

    //code by shiwanli----------初始化数据库
    lrd_init(&InitInfo, DBTypeVersion);
    lrd_initialize_db(LRD_DBTYPE_ORACLE, 3, 0);
    lrd_env_init(LRD_DBTYPE_ORACLE, &OraEnv1, 0, 0);
    lrd_ora8_handle_alloc(OraEnv1, SVCCTX, &OraSvc1, 0);
    lrd_ora8_handle_alloc(OraEnv1, SERVER, &OraSrv1, 0);
    lrd_ora8_handle_alloc(OraEnv1, SESSION, &OraSes1, 0);
    //code by shiwanli----------连接数据库,这里数据库服务名为lisx
    lrd_server_attach(OraSrv1, "lisx", -1, 0, 0);
    lrd_ora8_attr_set_from_handle(OraSvc1, SERVER, OraSrv1, 0, 0);
    //code by shiwanli----------设定数据库用户名密码checker
    lrd_ora8_attr_set(OraSes1, USERNAME, "checker", -1, 0);
    lrd_ora8_attr_set(OraSes1, PASSWORD, "checker", -1, 0);
    //code by shiwanli----------初始化连接session
    lrd_ora8_attr_set_from_handle(OraSvc1, SESSION, OraSes1, 0, 0);
    //code by shiwanli----------开始连接数据库
    lrd_session_begin(OraSvc1, OraSes1, 1, 0, 0);
    lrd_ora8_handle_alloc(OraEnv1, STMT, &OraStm1, 0);
    //code by shiwanli----------设定查询语句
    lrd_ora8_stmt(OraStm1, "select sum(prem) from lccont where prtno='120010100000470' \n", 1, 0, 0);
    //code by shiwanli----------执行查询语句
    //code by shiwanli----------第三个参数,执行次数
    //code by shiwanli----------第四个参数,跳过记录数
    //code by shiwanli----------第五个参数,返回执行影响的行数
    //code by shiwanli----------第九个参数,设置执行模式,0是默认值执行(不提交),16不执行,32代表执行且自动提交
    //code by shiwanli----------第十个参数,设置警告级别,0是默认值,代表错误,1代表警告
    lrd_ora8_exec(OraSvc1, OraStm1, 0, 0,&rownum, 0, 0, 0, 0, 1);
    //code by shiwanli----------绑定该列
    lrd_ora8_bind_col(OraStm1,&OraDef1,1,&NUM,0,0);
    //code by shiwanli----------绑定第二列         lrd_ora8_bind_col(OraStm1,&OraDef2,2,&NUM1,0,0);

    //code by shiwanli----------设定保存列中的某个数据到row中,第二个参数为第几列,第三个参数为第几行(只能保存一个值),最后一个参数就是你想要保存到的parameter名称
    lrd_ora8_save_col(OraStm1, 1, 1, 0, "ResultQuery");
    //code by shiwanli----------获取第二列并赋值      lrd_ora8_save_col(OraStm1, 2, 1, 0, "resultusername");
    //code by shiwanli----------获取结果集
    lrd_ora8_fetch(OraStm1, -2, 2, &rownum, 0, 2, 0, 0);
    //code by shiwanli----------打印结果
    lr_message("查询出的结果为: %s", lr_eval_string("{ResultQuery}"));
    //code by shiwanli----------释放连接数据库的各种变量
    lrd_handle_free(&OraStm1, 0);
    lrd_session_end(OraSvc1, OraSes1, 0, 0);
    lrd_server_detach(OraSrv1, 0, 0);
    lrd_handle_free(&OraEnv1, 0);


        strcat(Temp,lr_eval_string("{ResultQuery}"));
        lr_save_string(Temp,"Prem_value");
     lr_message("缴费金额为: %s", lr_eval_string("{Prem_value}"));
     return 0;
    }


    工作过程中发现我在某台机器上执行成功,但是我重新新建一个脚本就会报这样那样的错误。为了让看到后来人能少走弯路下面

    我将详细解释下如何才能成功执行此脚本。

     

    1、如果你是直接使用oracle(2-tier)协议创建的脚本的话那你需要修改

    vdf.h   print.inl  这两个文件【此两个文件中的内容我将在后面贴出来,这个博客不会贴附件。。。。。。】

       然后你再执行一下脚本,试一下是不是可以了?可以从数据库中查出数据了?

    2、如果你是使用web协议创建的脚本的话,需要修改的内容如下

      修改脚本目录下的.usr文件中的如下字段
    AdditionalTypes=Oracle,QTWeb
    ActiveTypes=Oracle,QTWeb
    GenerateTypes=Oracle,QTWeb
    修改脚本目录下的globals.h添加如下内容
    #include "vdf.h"
    #include "print.inl"
    拷贝vdf.h文件到脚本目录下
    拷贝print.inl文件到脚本目录下

    再执行下,看看应该是ok了吧。

    下面贴出vdf.h内容

    /* vdf.h */
    #ifndef VDF_H
    #define  VDF_H

    #define LRD_RECORDED_UNDER_WIN32
    #include "lrd.h"

    static LRD_INIT_INFO InitInfo = {LRD_INIT_INFO_EYECAT};
    static LRD_DEFAULT_DB_VERSION DBTypeVersion[] =
    {
        {LRD_DBTYPE_NONE, LRD_DBVERSION_NONE}
    };
    static unsigned long        uliRowsProcessed;
    static LRD_VAR_DESC      USER_D1 =
        {LRD_VAR_DESC_EYECAT, 1, 31, LRD_DBTYPE_ORACLE, {0, 1, 0}, DT_SZ};
    static unsigned long        uliFetchedRows;
    static LRD_VAR_DESC      ATTRIBUTE_D2 =
        {LRD_VAR_DESC_EYECAT, 1, 250, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      SCOPE_D3 =
        {LRD_VAR_DESC_EYECAT, 1, 250, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      NUMERIC_VALUE_D4 =
        {LRD_VAR_DESC_EYECAT, 1, 250, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      CHAR_VALUE_D5 =
        {LRD_VAR_DESC_EYECAT, 1, 250, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      DATE_VALUE_D6 =
        {LRD_VAR_DESC_EYECAT, 1, 250, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      CHAR_VALUE_D7 =
        {LRD_VAR_DESC_EYECAT, 1, 250, LRD_DBTYPE_ORACLE, {0, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      P1D8 =
        {LRD_VAR_DESC_EYECAT, 1, 8, LRD_DBTYPE_ORACLE, {1, 1, 0},
            DT_SF_STRIPPED_SPACES};
    static LRD_VAR_DESC      DECODE_A_A_1_2_D9 =
        {LRD_VAR_DESC_EYECAT, 1, 22, LRD_DBTYPE_ORACLE, {1, 1, 0},
            DT_NUMERIC};
    static LRD_VAR_DESC      USERCODE_D10 =
        {LRD_VAR_DESC_EYECAT, 15, 21, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      USERNAME_D11 =
        {LRD_VAR_DESC_EYECAT, 15, 21, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      COMCODE_D12 =
        {LRD_VAR_DESC_EYECAT, 15, 21, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      PASSWORD_D13 =
        {LRD_VAR_DESC_EYECAT, 15, 17, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      USERDESCRIPTION_D14 =
        {LRD_VAR_DESC_EYECAT, 15, 51, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      USERSTATE_D15 =
        {LRD_VAR_DESC_EYECAT, 15, 2, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      UWPOPEDOM_D16 =
        {LRD_VAR_DESC_EYECAT, 15, 11, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      CLAIMPOPEDOM_D17 =
        {LRD_VAR_DESC_EYECAT, 15, 3, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      OTHERPOPEDOM_D18 =
        {LRD_VAR_DESC_EYECAT, 15, 3, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      POPUWFLAG_D19 =
        {LRD_VAR_DESC_EYECAT, 15, 2, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      SUPERPOPEDOMFLAG_D20 =
        {LRD_VAR_DESC_EYECAT, 15, 2, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      OPERATOR_D21 =
        {LRD_VAR_DESC_EYECAT, 15, 61, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      MAKEDATE_D22 =
        {LRD_VAR_DESC_EYECAT, 15, 15, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      MAKETIME_D23 =
        {LRD_VAR_DESC_EYECAT, 15, 9, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      VALIDSTARTDATE_D24 =
        {LRD_VAR_DESC_EYECAT, 15, 15, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      VALIDENDDATE_D25 =
        {LRD_VAR_DESC_EYECAT, 15, 15, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      CERTIFYFLAG_D26 =
        {LRD_VAR_DESC_EYECAT, 15, 2, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      EDORPOPEDOM_D27 =
        {LRD_VAR_DESC_EYECAT, 15, 3, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static LRD_VAR_DESC      AGENTCOM_D28 =
        {LRD_VAR_DESC_EYECAT, 15, 21, LRD_DBTYPE_ORACLE, {1, 1, 0}, DT_SZ};
    static void FAR *           OraEnv1;
    static void FAR *           OraSvc1;
    static void FAR *           OraStm1;
    static void FAR *           OraStm2;
    static void FAR *           OraStm3;
    static void FAR *           OraStm4;
    static void FAR *           OraStm5;
    static void FAR *           OraStm6;
    static void FAR *           OraStm7;
    static void FAR *           OraBnd1;
    static void FAR *           OraDef1;
    static void FAR *           OraDef2;
    static void FAR *           OraDef3;
    static void FAR *           OraDef4;
    static void FAR *           OraDef5;
    static void FAR *           OraDef6;
    static void FAR *           OraDef7;
    static void FAR *           OraDef8;
    static void FAR *           OraDef9;
    static void FAR *           OraDef10;
    static void FAR *           OraDef11;
    static void FAR *           OraDef12;
    static void FAR *           OraDef13;
    static void FAR *           OraDef14;
    static void FAR *           OraDef15;
    static void FAR *           OraDef16;
    static void FAR *           OraDef17;
    static void FAR *           OraDef18;
    static void FAR *           OraDef19;
    static void FAR *           OraDef20;
    static void FAR *           OraDef21;
    static void FAR *           OraDef22;
    static void FAR *           OraDef23;
    static void FAR *           OraDef24;
    static void FAR *           OraDef25;
    static void FAR *           OraDef26;
    static void FAR *           OraDef27;
    static void FAR *           OraSrv1;
    static void FAR *           OraSes1;
    static void FAR *           OraSes2;
    #endif

     


    下面贴出print.inl文件内容

    #ifndef INL_H
    #define  INL_H
    LRD_ORA8_PRINT_ROW_PROTO(PrintRow2)
    {
        LRDRET gjLRDRet = LRDRET_I_OK;
        char szUSER_D1[256];
    lrd_to_printable(&USER_D1, muliRowIndex, szUSER_D1, 256, "");
        lr_debug_message(LR_MSG_CLASS_RESULT_DATA, "%s", szUSER_D1);
        return gjLRDRet;
    }

    LRD_ORA8_PRINT_ROW_PROTO(PrintRow6)
    {
        LRDRET gjLRDRet = LRDRET_I_OK;
        char szDECODE_A_A_1_2_D9[256];
    lrd_to_printable(&DECODE_A_A_1_2_D9, muliRowIndex, szDECODE_A_A_1_2_D9, 256, "");
        lr_debug_message(LR_MSG_CLASS_RESULT_DATA,a "%s", szDECODE_A_A_1_2_D9);
        return gjLRDRet;
    }

    LRD_ORA8_PRINT_ROW_PROTO(PrintRow8)
    {
        LRDRET gjLRDRet = LRDRET_I_OK;
        char szUSERCODE_D10[256];
        char szUSERNAME_D11[256];
        char szCOMCODE_D12[256];
        char szPASSWORD_D13[256];
        char szUSERDESCRIPTION_D14[256];
        char szUSERSTATE_D15[256];
        char szUWPOPEDOM_D16[256];
        char szCLAIMPOPEDOM_D17[256];
        char szOTHERPOPEDOM_D18[256];
        char szPOPUWFLAG_D19[256];
        char szSUPERPOPEDOMFLAG_D20[256];
        char szOPERATOR_D21[256];
    lrd_to_printable(&USERCODE_D10, muliRowIndex, szUSERCODE_D10, 256, "");
    lrd_to_printable(&USERNAME_D11, muliRowIndex, szUSERNAME_D11, 256, "");
    lrd_to_printable(&COMCODE_D12, muliRowIndex, szCOMCODE_D12, 256, "");
    lrd_to_printable(&PASSWORD_D13, muliRowIndex, szPASSWORD_D13, 256, "");
    lrd_to_printable(&USERDESCRIPTION_D14, muliRowIndex, szUSERDESCRIPTION_D14, 256, "");
    lrd_to_printable(&USERSTATE_D15, muliRowIndex, szUSERSTATE_D15, 256, "");
    lrd_to_printable(&UWPOPEDOM_D16, muliRowIndex, szUWPOPEDOM_D16, 256, "");
    lrd_to_printable(&CLAIMPOPEDOM_D17, muliRowIndex, szCLAIMPOPEDOM_D17, 256, "");
    lrd_to_printable(&OTHERPOPEDOM_D18, muliRowIndex, szOTHERPOPEDOM_D18, 256, "");
    lrd_to_printable(&POPUWFLAG_D19, muliRowIndex, szPOPUWFLAG_D19, 256, "");
    lrd_to_printable(&SUPERPOPEDOMFLAG_D20, muliRowIndex, szSUPERPOPEDOMFLAG_D20, 256, "");
    lrd_to_printable(&OPERATOR_D21, muliRowIndex, szOPERATOR_D21, 256, "");
        lr_debug_message(LR_MSG_CLASS_RESULT_DATA, "%s, %s, %s, %s, %s, %s,"
            "%s, %s, %s, %s, %s, %s, ...", szUSERCODE_D10, szUSERNAME_D11,
            szCOMCODE_D12, szPASSWORD_D13, szUSERDESCRIPTION_D14,
            szUSERSTATE_D15, szUWPOPEDOM_D16, szCLAIMPOPEDOM_D17,
            szOTHERPOPEDOM_D18, szPOPUWFLAG_D19, szSUPERPOPEDOMFLAG_D20,
            szOPERATOR_D21);
        return gjLRDRet;
    }

    #ifndef CCI
    BEGIN_VUSER_DECLARATION
        DECLARE_VUSER_RUN("Vuser Run", Actions)
    END_VUSER_DECLARATION
    #endif

    #endif

  • 转-LoadRunner动态访问Oracle数据库+多协议录制解决方案

    2011-03-01 10:37:05

    一、LoadRunner9.0动态访问Oracle数据库


     #include "lrd.h"


     static LRD_INIT_INFO lnitlnfo = {LRD_INIT_INFO_EYECAT};

     static LRD_DEFAULT_DB_VERSION DBTypeVersion[] = {{LRD_DBTYPE_NONE,LRD_DBVERSION_NONE}};

       //这里的LRD_VAR_DESC数据结构声明是很重要的,他是用来存储sql结果数据集的结构体,第一个参数头文件中就是这么写的,第二个参数是最 大行数,第三个参数是每一行的最大长度,如果获得的查询结果比定义的长,运行时就会报错,提示列被截断,最后一个参数是查询结果的类型,可以再帮助中的索 引输入data types, database,列出的表格中是各种变量类型的名称

     static LRD_VAR_DESC OBJECT_NAME_D1;

     static LRD_VAR_DESC NUM ={LRD_VAR_DESC_EYECAT, 10, 32, LRD_DBTYPE_ORACLE, {1, 1, 0},DT_LONG_VARCHAR};

        //定义初始化数据库的各种变量

     static void FAR * OraEnv1;

     static void FAR * OraSvc1;

     static void FAR * OraSrv1;

     static void FAR * OraSes1;

     static void FAR * OraStm1;

     static void FAR * OraDef1;

     static  unsigned long uliFetchedRows;

     unsigned long     rownum;

     //初始化数据库

     lrd_init(&lnitlnfo, DBTypeVersion);

     lrd_initialize_db(LRD_DBTYPE_ORACLE,3,0);

     lrd_env_init(LRD_DBTYPE_ORACLE, &OraEnv1,0,0);

     lrd_ora8_handle_alloc(OraEnv1,SVCCTX,&OraSvc1,0);

     lrd_ora8_handle_alloc(OraEnv1,SERVER,&OraSrv1,0);

     lrd_ora8_handle_alloc(OraEnv1,SESSION,&OraSes1,0);

     //连接数据库,我的是oracle,odbc中连接数据库名称就是这个

     lrd_server_attach(OraSrv1,"数据库名",-1,0,0);

     lrd_ora8_attr_set_from_handle(OraSvc1,SERVER,OraSrv1,0,0);

     //用户名和密码

     lrd_ora8_attr_set(OraSes1,USERNAME,"用户名",-1,0);

     lrd_ora8_attr_set(OraSes1,PASSWORD,"密码",-1,0);

     //初始化连接session

     lrd_ora8_attr_set_from_handle(OraSvc1,SESSION,OraSes1,0,0);

     //开始连接数据库

     lrd_session_begin(OraSvc1,OraSes1,1,0,0);

     lrd_ora8_handle_alloc(OraEnv1,STMT,&OraStm1,0);

     //设定sql语句

     lrd_ora8_stmt(OraStm1, "select XXX from XXX", 1, 0, 0);

     //执行sql语句,并且将结果行数返回到rownum中

     lrd_ora8_exec(OraSvc1, OraStm1, 0, 0,&rownum, 0, 0, 0, 0, 1);

     //绑定该列

     lrd_ora8_bind_col(OraStm1,&OraDef1,1,&NUM,0,0);
    //设定保存列中的某个数据到row中,第二个参数为第几列,第三个参数为第几行(只能保存一个值),最后一个参数就是你想要保存到的parameter名称

     lrd_ora8_save_col(OraStm1, 1, 1, 0, "resu1");

     lrd_ora8_fetch(OraStm1, -2, 2, &rownum, 0, 2, 0, 0);

     lr_error_message("sql result: %s", lr_eval_string("{resu1}"));

     

     //释放连接数据库的各种变量

     lrd_handle_free(&OraStm1, 0);

     lrd_session_end(OraSvc1, OraSes1, 0, 0);

     lrd_server_detach(OraSrv1, 0, 0);

     lrd_handle_free(&OraEnv1, 0);


     (1)首先要安装oracle客户端,通过客户端连接oracle数据库。如果不装数据库客户端会报错,如下:illegal character `\0241'

     (2)写访问oracle数据库的脚本用oracle(2-tier)+web协议新建脚本,否则执行时会报错,如下:Error -- Unresolved symbol : lrdfnc_init
     然后将录制的脚本拷到写好的访问oracle数据库脚本中。

     (3)数据库连接不上
     Action.c(47): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=12154:
     Action.c(47): Error:     ORA-12154: TNS: 无法解析指定的连接标识符
     Action.c(47): server_attach: ERROR, return-code=LRDE2009.  ServerHandle=OraSrv1, ServerID="XXXX"

     (4)sql语句错误,报如下错误
     Action.c(81): Error: lrdo_ora8_fetch: "OCIStmtFetch" return-code=OCI_ERROR, error-code=24338:
     Action.c(81): Error:     ORA-24338: 未执行语句句柄
     Action.c(81): lrd_ora8_fetch: ERROR, return-code=LRDE2009.  StmtHandle=OraStm1, 0 row(s) fetched


     (5) 如果用controller执行涉及好的脚本报如下错误:you do not have a license for this vuser type please contact mercury interactive to renew your license
        解决方案:原因是由于目前使用的License不支持Socket场景运行,将License替换一下就可以了。
        使用的License也就是目前网络上比较通用的两个:
        global 100user:AEAMAUIK-YAFEKEKJJKEEA-BCJGI
        10000 web clients:AEABEXFR-YTIEKEKJJMFKEKEKWBRAUNQJU-KBYGB
        一开始使用的10000 web clients的,出现该问题,后来替换成global 100 user 就可以了。具体视情况而定。


       二、多协议录制脚本,协议web(http/html),ie打开后没有反应了
       在recording options中advanced里选择support charset utf-8

    三、多协议录制脚本,协议web(http/html),ld根本没反应
        修改ie设置,工具-高级,把启动第三方浏览器扩展(需要重启动)

    四、单协议录制脚本,系统中有些东西出不来
        原因:客户端安装了插件,插件会向服务器发送请求

  • 转-使用LR测试Oracle数据库经验总结

    2011-02-28 21:22:34

    1、我将登录SQL*Plus的操作也录制在Action中,添加迭代运行会报错,提示如下:

    Action.c(11): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=24309:
    Action.c(11): Error:     ORA-24309: 已连接至服务器
    Action.c(11): server_attach: ERROR, return-code=LRDE2009.  ServerHandle=OraSrv1, ServerID="customs"
    Abort was called from an action.

    解决办法:1、将登陆SQL*Plus的操作录制在vuser_init中;2、同时录制退出SQL*Plus的操作

    2、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:
    Action.c(37): Error: lrdo_ora8_exec: "OCIStmtExecute" return-code=OCI_ERROR,error-code=00054:

    解决方法:把lrd_ora8_exec(OraSvc1, OraStm<i>, 0, 0, &uliRowsProcessed, 0, 0, 0, 0, 0);
    改为:lrd_ora8_exec(OraSvc1, OraStm<i>, 0, 0, &uliRowsProcessed, 0, 0, 0, 0, 1);

    3、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:
    Action.c(13): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=12514:

    解决办法:开启数据库服务器监听,启动数据库服务器。

    4、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:
    Action.c(26): Error: C interpreter run time error: Action.c (26): Error -- Unresolved symbol : lrd_session_begin.

    解决办法:在vuser_init中添加 #include "lrd.h"

    5、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:
    Starting iteration 1.
    Starting action Action.
    Action.c(13): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=12541:
    Action.c(13): Error: ORA-12541: TNS: 没有监听器
    Action.c(13): server_attach: ERROR, return-code=LRDE2009. ServerHandle=OraSrv1, ServerID="lr"

    解决办法:启动数据库服务器,开启listener。如果需要使用isqlplus的话,开启服务:$isqlplusctl start

    6、测试压力上来以后,会有好多Vuser出现Error,具体报错如下:
    Action.c(13): Error: lrdo_server_attach: "OCIServerAttach" return-code=OCI_ERROR, error-code=12514:
    Action.c(13): Error: ORA-12514: TNS: 监听进程不能解析在连接描述符中给出的 SERVICE_NAME
    Action.c(13): server_attach: ERROR, return-code=LRDE2009. ServerHandle=OraSrv1, ServerID="lr"

    解决办法:检查压力机Oracle客户端解析文件tnsnames.ora,确保正确配置了这个文件。
    检查测试的数据库服务器,确保正确配置了监听,并已经开启。

    7、 使用Controller产生负载,最后测试数据只有以下4条性能曲线:Running Vusers、Trans Response Time、Trans/Sec(Passed)、Total Trans/Sec(Passed)、其余的Throughput、HTTP Responses per Second、Connections等等性能统计项均显示为灰色,没有统计数据

    解决办法:因为sqlplus.exe是一个win32程序,那么在测试的过程中只统计上面的四项,它不会统计Throughput这些Web测试才有的性能曲线。
    所以,如果想对数据库服务器测试的更全面,这个时候就要使用isqlplus来发出查询。
    1)在DB server上启动isqlplus服务:isqlplusctl start
    2)启动VuGen,在客户端(压力机)启动isqlplus的Web页面
    3)输入用户名、密码,我这里采用scott/tiger@lr
    OK!录制完成之后,产生压力执行测试。顺利结束之后,可以看到如下曲线已经抓取出来了:
    Throughput、HTTP Responses per Second、Connections
    剩余的工作就是如何分析性能数据了。

  • 转-Loadrunner测试MySql数据库性能,测试SQL语句性能的脚本例子

    2011-02-28 20:17:12

    此代码为Loadrunner 8 通过C API类型的Vuser 测试MySQL性能,或者测试sql语句性能的脚本

    view plaincopy to clipboardprint?
    /*需要的表结构如下
    CREATE TABLE `test_data` (
    `order_id` BIGINT UNSIGNED NOT NULL COMMENT 'Order numbers. Must be unique.',
    `status` BOOL NOT NULL DEFAULT '0' COMMENT 'Whether data has been used or not. A value of 0 means FALSE.',
    `date_used` DATETIME NULL COMMENT 'Date/time that the data was used.',
    UNIQUE (
        `order_id`
    )
    ) ENGINE = innodb COMMENT = 'LoadRunner test data';
    */
    Action()
    {
    int rc;  
    int db_connection; // 数据库连接
    int query_result; // 查询结果集 MYSQL_RES
    char** result_row; // 查询的数据衕
      
    char *server = "localhost";
    char *user = "root";
    char *password = "123456";
    char *database = "test";
    int port = 3306;
    int unix_socket = NULL;  
    int flags = 0;  
      
    // 找到libmysql.dll的所在位置.
    rc = lr_load_dll("C:\\Program Files\\MySQL\\MySQL Server 5.1\\bin\\libmysql.dll");
    if (rc != 0) {
        lr_error_message("Could not load libmysql.dll");
        lr_abort();
    }
      
    // 创建MySQL对象
    db_connection = mysql_init(NULL);
    if (db_connection == NULL) {
        lr_error_message("Insufficient memory");
        lr_abort();
    }
      
    // 连接到MySQL数据库
    rc = mysql_real_connect(db_connection, server, user, password, database, port, unix_socket, flags);
    if (rc == NULL) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
      
    // 向数据库插入数据
    // 此处的 {ORDER_ID} 是一个参数,简单测试时可以用一个常数代替
    lr_save_string (lr_eval_string("INSERT INTO test_data (order_id) VALUES ({ORDER_ID})"),"paramInsertQuery");  
    rc = mysql_query(db_connection, lr_eval_string("{paramInsertQuery}"));
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
      
    // 从数据库读取一个数据并显示
    rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1");
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    query_result = mysql_use_result(db_connection);
    if (query_result == NULL) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    // 如果结果集包含多行数据,需要多次调用 mysql_fetch_row 直到返回NULL
    result_row = (char **)mysql_fetch_row(query_result);  
    if (result_row == NULL) {
        lr_error_message("Did not expect the result set to be empty");
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    // 保存参数,用于删除这行数据
    lr_save_string(result_row[0], "paramOrderID");
    lr_output_message("Order ID is: %s", lr_eval_string("{paramOrderID}"));
    mysql_free_result(query_result);
      
    // 在事务里更新一行数据,需要用InnoDB引擎
    rc = mysql_query(db_connection, "BEGIN"); //启动事务
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    // 使用 "FOR UPDATE" 锁住要更新的数据行
    rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1 FOR UPDATE");  
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    query_result = mysql_use_result(db_connection);
    if (query_result == NULL) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    result_row = (char **)mysql_fetch_row(query_result);  
    if (result_row == NULL) {
        lr_error_message("没有查询到结果");
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    lr_save_string(result_row[0], "paramOrderID");
    lr_output_message("Order ID is: %s", lr_eval_string("{paramOrderID}"));
    mysql_free_result(query_result);
    lr_save_string(lr_eval_string("UPDATE test_data SET status=TRUE, date_used=NOW() WHERE order_id='{paramOrderID}'"),"paramUpdateQuery");
    rc = mysql_query(db_connection, lr_eval_string("{paramUpdateQuery}"));
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    rc = mysql_query(db_connection, "COMMIT"); // 提交事务
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
      
    // 再次查找数据,应该为空了,因为前面的事务更新了标志
    rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1");
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    query_result = mysql_use_result(db_connection);
    if (query_result == NULL) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    result_row = (char **)mysql_fetch_row(query_result);
    if (result_row == NULL) {
        lr_output_message("Result set is empty as expected");
        mysql_free_result(query_result);
    } else {
        lr_error_message("Did not expect the result set to contain any rows");
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
      
    // 删除数据
    lr_save_string(lr_eval_string("DELETE FROM test_data WHERE order_id = '{paramOrderID}'"),"paramDeleteQuery");
    rc = mysql_query(db_connection, lr_eval_string("{paramDeleteQuery}"));
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
      
    // 释放MySQL资源
    mysql_close(db_connection);
        return 0;
    }
    /*需要的表结构如下
    CREATE TABLE `test_data` (
    `order_id` BIGINT UNSIGNED NOT NULL COMMENT 'Order numbers. Must be unique.',
    `status` BOOL NOT NULL DEFAULT '0' COMMENT 'Whether data has been used or not. A value of 0 means FALSE.',
    `date_used` DATETIME NULL COMMENT 'Date/time that the data was used.',
    UNIQUE (
        `order_id`
    )
    ) ENGINE = innodb COMMENT = 'LoadRunner test data';
    */
    Action()
    {
    int rc;
    int db_connection; // 数据库连接
    int query_result; // 查询结果集 MYSQL_RES
    char** result_row; // 查询的数据衕

    char *server = "localhost";
    char *user = "root";
    char *password = "123456";
    char *database = "test";
    int port = 3306;
    int unix_socket = NULL;
    int flags = 0;

    // 找到libmysql.dll的所在位置.
    rc = lr_load_dll("C:\\Program Files\\MySQL\\MySQL Server 5.1\\bin\\libmysql.dll");
    if (rc != 0) {
        lr_error_message("Could not load libmysql.dll");
        lr_abort();
    }

    // 创建MySQL对象
    db_connection = mysql_init(NULL);
    if (db_connection == NULL) {
        lr_error_message("Insufficient memory");
        lr_abort();
    }

    // 连接到MySQL数据库
    rc = mysql_real_connect(db_connection, server, user, password, database, port, unix_socket, flags);
    if (rc == NULL) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }

    // 向数据库插入数据
    // 此处的 {ORDER_ID} 是一个参数,简单测试时可以用一个常数代替
    lr_save_string (lr_eval_string("INSERT INTO test_data (order_id) VALUES ({ORDER_ID})"),"paramInsertQuery");
    rc = mysql_query(db_connection, lr_eval_string("{paramInsertQuery}"));
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }

    // 从数据库读取一个数据并显示
    rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1");
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    query_result = mysql_use_result(db_connection);
    if (query_result == NULL) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    // 如果结果集包含多行数据,需要多次调用 mysql_fetch_row 直到返回NULL
    result_row = (char **)mysql_fetch_row(query_result);
    if (result_row == NULL) {
        lr_error_message("Did not expect the result set to be empty");
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    // 保存参数,用于删除这行数据
    lr_save_string(result_row[0], "paramOrderID");
    lr_output_message("Order ID is: %s", lr_eval_string("{paramOrderID}"));
    mysql_free_result(query_result);

    // 在事务里更新一行数据,需要用InnoDB引擎
    rc = mysql_query(db_connection, "BEGIN"); //启动事务
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    // 使用 "FOR UPDATE" 锁住要更新的数据行
    rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1 FOR UPDATE");
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    query_result = mysql_use_result(db_connection);
    if (query_result == NULL) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    result_row = (char **)mysql_fetch_row(query_result);
    if (result_row == NULL) {
        lr_error_message("没有查询到结果");
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    lr_save_string(result_row[0], "paramOrderID");
    lr_output_message("Order ID is: %s", lr_eval_string("{paramOrderID}"));
    mysql_free_result(query_result);
    lr_save_string(lr_eval_string("UPDATE test_data SET status=TRUE, date_used=NOW() WHERE order_id='{paramOrderID}'"),"paramUpdateQuery");
    rc = mysql_query(db_connection, lr_eval_string("{paramUpdateQuery}"));
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    rc = mysql_query(db_connection, "COMMIT"); // 提交事务
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }

    // 再次查找数据,应该为空了,因为前面的事务更新了标志
    rc = mysql_query(db_connection, "SELECT order_id FROM test_data WHERE status IS FALSE LIMIT 1");
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }
    query_result = mysql_use_result(db_connection);
    if (query_result == NULL) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }
    result_row = (char **)mysql_fetch_row(query_result);
    if (result_row == NULL) {
        lr_output_message("Result set is empty as expected");
        mysql_free_result(query_result);
    } else {
        lr_error_message("Did not expect the result set to contain any rows");
        mysql_free_result(query_result);
        mysql_close(db_connection);
        lr_abort();
    }

    // 删除数据
    lr_save_string(lr_eval_string("DELETE FROM test_data WHERE order_id = '{paramOrderID}'"),"paramDeleteQuery");
    rc = mysql_query(db_connection, lr_eval_string("{paramDeleteQuery}"));
    if (rc != 0) {
        lr_error_message("%s", mysql_error(db_connection));
        mysql_close(db_connection);
        lr_abort();
    }

    // 释放MySQL资源
    mysql_close(db_connection);
    return 0;
    }

  • 转-Loadrunner 测试sql语句性能(sql Server)

    2011-02-28 20:06:24

    本次通过loadrunner录制Sql Server介绍一下如何测试一个sql语句或存储过程的执行性能。

    主要分如下几个步骤完成:

    第一步、测试准备

    第二步、配置ODBC数据源

    第三步、录制SQL语句在Sql Server查询分析器中的运行过程

    第四步、优化录制脚本,设置事务

    第五步、改变查询数量级查看SQL语句的性能

    第六步、在controller中运行脚本

    转载请注明出处:http://www.51testing.com/?41972

    下面开始具体的介绍:

    测试准备阶段我们首先要确认测试数据库服务器:我们可以在本地安装SQL SERVER数据库服务端及客户端,也可以确定一台装好的SQL SERVER服务器。

    接下来,准备测试数据:对数据库测试时我们要考虑的不是SQL语句是否能够正确执行,而是在某数量级的情况下SQL语句的执行效率及数据库服务的运行情况,所以我们分别准备不同数量级的测试数据,即根据实际的业务情况预估数据库中的记录数,在本次讲解中我们不考虑业务逻辑也不考虑数据表之间的关系,我们只建立一张表,并向此表中加入不同数量级的数据,如分别加入1000条、10000条、50000条、100000条数据查看某SQL语句的执行效率。

    在查询分析器中运行如下脚本:

    --创建测试数据库

    create database loadrunner_test;

    use loadrunner_test

    --创建测试数据表

    create table test_table

    (username varchar(50),sex int,age int,address varchar(100),post int)

    --通过一段程序插入不同数量级的记录,具体的语法在这里就不多说了

    declare   @i   int  

      set   @i=0  

      while   @i<1000    //循环1000次,可以根据测试数据情况改变插入条数

      begin

          BEGIN TRAN T1

          insert into test_table (username,sex,age,address,post) values ('户瑞海'+cast(@i as varchar),@i-1,@i+1,'北京市和平里'+cast(@i as varchar)+'',123456); 

          IF @@ERROR <> 0

             begin

               rollback;

               select @@error

             end

          else

             begin

               commit;

               set   @i   =   @i+1

             end    

      end

    转载请注明出处:http://www.51testing.com/?41972

    好了,执行完上述语句后,建立的数据表中已经有1000条记录了,下面进行第二步的操作,配置ODBC数据源,为了能让loadrunner能够通过ODBC协议连接到我们建立的SQL SERVER数据路,我们需要在本机上建立ODBC数据源,建立方法如下:

    控制面板性能和维护管理工具数据源(ODBC--添加,在列表中选择SQL SERVER点击完成,根据向导输入数据源名称,链接的服务器,下一步,输入链接数据库的用户名和密码,更改链接的数据库,完成ODBC的配置,如果配置正确的话,在最后一步点击“测试数据源”,会弹出测试成功的提示。

    配置好ODBC数据源后就要录制SQL语句在查询分析器中的执行过程了:

    1、  打开loadrunner,选择ODBC协议

    2、  start recording中的application type 选择win32 applicationprogram to record中录入SQL SERVER查询分析器的路径“..\安装目录\isqlw.exe

    3、  开始录制,首先通过查询分析器登录SQL SERVER,在打开的查询分析器窗口中输入要测试的SQL语句,如“select * from test_table;

    4、  在查询分析器中执行该语句,执行完成后,结束录制

    好了,现在就可以看到loadrunner生成的脚本了(由于脚本过长,在这里就不粘贴了,有需要的朋友可以加我QQ,我把脚本发给你们),通过这些语句,我们可以看出,登录数据库的过程、执行SQL语句的过程

    转载请注明出处:http://www.51testing.com/?41972

    接下来,我们来优化脚本,我们分别为数据库登录部分和执行SQL语句的部分加一个事物,在增加一个double的变量获取事务执行时间,简单内容如下:

    Action()

    {  double trans_time;  //定义一个double型变量用来保存事务执行时间

       lr_start_transaction("sqserver_login");  //设置登录事务的开始

    lrd_init(&InitInfo, DBTypeVersion);   //初始化链接(下面的都是loadrunner生成的脚本了,大家可以通过帮助查到每个函数的意思)

           lrd_open_context(&Ctx1, LRD_DBTYPE_ODBC, 0, 0, 0);

           lrd_db_option(Ctx1, OT_ODBC_OV_ODBC3, 0, 0);

           lrd_alloc_connection(&Con1, LRD_DBTYPE_ODBC, Ctx1, 0 /*Unused*/, 0);

        ………………

    trans_time=lr_get_transaction_duration( "sqserver_login" ); //获得登录数据库的时间

        lr_output_message("sqserver_login事务耗时 %f ", trans_time); //输出该时间

        lr_end_transaction("sqserver_login", LR_AUTO);  //结束登录事务

    lr_start_transaction("start_select");//开始查询事务

    lrd_cancel(0, Csr2, 0 /*Unused*/, 0);

    lrd_stmt(Csr2, "select * from test_table;\r\n", -1, 1, 0 /*None*/, 0);//此句为执行的SQL

    lrd_bind_cols(Csr2, BCInfo_D42, 0);

    lrd_fetch(Csr2, -10, 1, 0, PrintRow24, 0);

    ……………..

    trans_time=lr_get_transaction_duration( "start_select" ); //获得该SQL的执行时间

    lr_output_message("start_select事务耗时 %f ", trans_time); //输出该时间

    lr_end_transaction("start_select", LR_AUTO); //结束查询事务

    优化后,在执行上述脚本后,就可以得到登录到数据库的时间及运行select * from test_table这条语句的时间了,当然我们也可以根据实际情况对该条语句进行参数化,可以测试多条语句的执行时间,也可以将该语句改为调用存储过程的语句来测试存储过程的运行时间。

    接下来把该脚本在controller中运行,设置虚拟用户数,设置集合点,这些操作我就不说了,但是值得注意的是,没有Mercury 授权的SQL SERVER用户license,在运行该脚本时回报错,提示“You do not have a license for this Vuser type.

    Please contact Mercury Interactive to renew your license.”我们公司穷啊买不起loadrunner,所以我也无法继续试验,希望有license朋友们监控一下运行结果!

    最起码在VUGen中运行该脚本我们可以得到任意一个SQL语句及存储过程的执行时间,如果我们测试的B/S结构的程序,我们也可以通过HTML协议录制的脚本在CONTROLLER中监控SQL SERVER服务器的性能情况,这样两方面结合起来就可以对数据库性能做一个完整的监控了。

    本人对LOADRUNNER也是在摸索中,如果文章有写的不对,或理解错误的地方请指出,不甚感激

  • LoadRunner配置方案

    2010-10-09 14:57:07

    1.配置方案运行时设置

      选择“Tools”>“Options”。在“Options”对话框有“Run-Time Settings”(运行时设置)、“Timeout”(超时)、“Run-Time File Storage”(运行时文件存储)、“Path Translation Table”(路径转换表)等选项卡。

      (1)“Run-Time Settings”选项卡

      “Run-Time Settings”(运行时设置)选项卡如图2-52所示。

      ● Vuser Quota(Vuser配额):要防止系统过载,可以设置Vuser活动的配额。Vuser配额适用于所有负载生成器上的Vuser。其中,“Number of Vusers that may be initialized at one time all load generators”(一次可以初始化的Vuser数——所有负载生成器)用来设置负载生成器一次可以初始化的最大Vuser数,默认的最大数目为999。

      软件测试工具

      图2-52  运行时设置

      ● When stopping Vusers:此组合框中的选项用于控制在单击“停止”按钮时Vuser停止运行的方式。其选项依次为:

      Ø Wait for the current iteration to end before stopping(退出前等待当前迭代结束):指示LoadRunner允许Vuser在停止前完成正在运行的迭代。Vuser将移动到“正在逐步退出”状态,然后逐渐退出方案。

      Ø Wait for the current action to end before stopping(退出前等待当前操作结束):指示LoadRunner允许Vuser在停止前完成正在运行的操作。Vuser将移动到“正在逐步退出”状态,然后逐渐退出方案。

      Ø Stop immediately(立即停止):指示LoadRunner立即停止运行Vuser。Vuser将移动到“正在退出”状态,然后立即退出方案。

      ● Use random sequence with seed:勾选此复选框,表示允许LoadRunner使用随机顺序的种子数。每个种子值代表用于测试执行的一个随机值顺序。每当使用该种子值时,会将相同顺序的值分配给方案中的Vuser。该设置适用于使用Random方法从数据文件中分配值的参数化Vuser脚本。它还将影响录制的思考时间的随机百分比,如果在测试执行中发现问题,并且要使用相同的随机值顺序重复该测试,请启用该选项。

      (2)“TimeOut”选项卡

      “TimeOut”(超时)选项卡如图2-53所示。“Command Timeout”(命令超时)是各种LoadRunner命令的最长时间限制。在控制台发出命令时,可以设置负载生成器或Vuser执行该命令的最长时间。如果在超时间隔内没有完成该命令,控制台将发布一条错误消息。

      软件测试工具

      图2-53  超时设置

      ● Enable timeout checks:即启用超时检查,指示LoadRunner在控制台发出命令后监视负载生成器和Vuser的状态。如果负载生成器或Vuser在指定的超时间隔内没有完成命令,控制台将发布一条错误消息。如果禁用超时限制,LoadRunner将无限长地等待负载生成器进行连接和断开连接,并且等待执行Initialize、Run、Pause和Stop命令。

      ● Connect:在此数值框中输入LoadRunner等待连接到任何负载生成器的时间限制值。如果在该时间内连接不成功,负载生成器的状态将更改为“失败”,默认连接超时是120秒。

      ● Disconnect:在此数值框中输入LoadRunner等待从任何负载生成器断开连接的时间限制值。如果在该时间内断开连接不成功,负载生成器的状态将更改为“失败”。默认的断开连接超时是120秒。

      LoadRunner承认活动Vuser的数量会影响超时值。例如,1000个Vuser尝试初始化将比10个Vuser尝试初始化花费更长的时间。LoadRunner将基于活动Vuser的数量向指定的超时值中添加内部值。

      ● Init:在此数值框中输入Initialize命令的超时值,默认的时间限制是180秒。

      ● Run:在此数值框中输入Run命令的超时值,默认的时间限制是120秒。

      ● Pause:在此数值框中输入Pause命令的超时值,默认的时间限制是120秒。

      ● Stop:在此数值框中输入Stop命令的超时值,默认的时间限制是120秒。

      ● Update Vuser elapsed time every(更新Vuser已用时间):指定LoadRunner更新在“Vuser”对话框中的“Elapsed Time”(已用时间)列中显示的值的频率。默认每隔4秒更新一次Vuser已用时间。

      如果选择一个Vuser并单击“Init”(初始化)按钮,LoadRunner将检查该Vuser在180秒(默认的“初始化”超时时间)内是否达到了“就绪”状态;如果没有达到,控制台将发布一条消息,指出该“初始化”命令超时。

    (3)“Run-Time File Storage”选项卡

      “Run-Time File Storage”(运行时文件存储)选项卡页面如图2-54所示。

      软件测试工具

      图2-54  运行时文件存储设置

      存储的脚本和结果可以使用下列选项之一:

      ● On the current Vuser machine(在当前Vuser计算机上):指示控制台将运行时文件保存在运行Vuser脚本的计算机上。在基于NT的计算机上,这些结果将保存到由TEMP或TMP环境变量定义的目录中。在UNIX计算机上,这些结果将保存到由 TMPDIR环境变量定义的目录中。如果没有定义TMPDIR环境变量,这些结果将保存到/tmp目录。

      ● On a shared network drive(在共享网络驱动器上):指示控制台将方案结果和/或Vuser脚本保存在共享网络驱动器上。共享网络驱动器是控制台和方案中的所有负载生成器对其拥有读写权限的驱动器。如果选择将结果保存到共享网络驱动器,可能需要执行路径转换。路径转换确保远程负载生成器可以识别指定的结果目录。如果指定所有Vuser在某个共享位置上直接访问其Vuser脚本,则在运行时不会传输任何脚本文件。该替代方法在以下两种情况可能很有用:

      Ø 文件传输设备无法工作。

      Ø  Vuser脚本文件太大,因此要花费很长时间进行传输。切记,Vuser脚本文件在方案运行期间仅传输一次。

      (4)“Path Translation Table”选项卡

      “Path Translation Table(路径转换表)”选项卡如图2-55所示。

      软件测试工具

      图2-55  路径转换表

      如果指定了运行时文件存储的共享网络驱动器,可能需要执行“路径转换”,路径转换是LoadRunner用来转换远程路径名的一种机制。在典型的性能测试设备配置方案中,根据实际情况,多台负载生成器(计算机)会以不同方式映射共享网络驱动器。

      2.运行环境设置

      操作后出现“Run-Time Setting”窗口,其中有不同的标签页。下面对运行时经常需要配置的标签页进行简要的配置说明。

      (1)“General:Miscellaneous”标签页(如图2-56所示)

      此界面为运行期间针对某些特殊功能,例如出现错误时如何处理等的一些辅助设置,一般的情况下不需要改动,其中有三项供用户设置。

      软件测试工具

      图2-56  环境设置

      “Error Handing”栏设置LoadRunner在遇到错误时的处理方法,一般情况下不需要改动。此选项下有三个复选框,分别为运行期间遇到错误不同的处理方法,

      ● Continue on error:选择此项后,如果运行时出现错误,将继续执行脚本,不会因为错误出现而停止,以此来保证脚本整个运行过程的完整性。

      ● Fail open transactions on lr_error message:选择此项后,如果运行时出现错误,系统会在事先脚本中插入的lr_error_message函数中显示出错误,此项需要与一些函数进行配合使用。

      ● Generate snapshot on error:选择此项后,如果运行时出现错误,系统会根据错误的级别将错误界面形成快照记录下来,运行结束后可以打开错误窗口进行查看。

      “Multithreading”栏用于确定Vuser运行时为多线程还是多进程,默认是多线程,一般不需要修改。如果选择“Run Vuser as a process”,则场景运行时会为每个Vuser创建一个进程;如果选择“Run Vuser as a thread”,则会将每个Vuser作为一个线程来运行,在任务管理器中只看到一个mmdrv.exe,这种方式的运行效率更高,能造成更大的压力。

      “Automatic Transactions”栏默认选择的是第一项“Define any actions as a transcation”,但如果需要把脚本的每一步都当作事务,可以选择第二项“Define any step as a transcation”,这样可以省去多次添加事务的烦琐操作。

    (2)“General:Think Time”标签页(如图2-57所示)

      软件测试工具

      图2-57  思考时间设置

      ● Ignore think time(忽略录制思考时间):选择该项,VuGen在脚本回放过程中将不执行Lr_think_time()函数,这样将给服务器造成更大的压力。

      ● Replay think time(使用录制思考时间):如选中该项,依次有以下4种选择:

      Ø  As record:按照录制过程中的Think Time值回放脚本,使用lr_think_time函数中显示的参数。

      Ø  Multiply recorded think time by:按照录制过程中的Think Time值的整数倍回放脚本,这种方法可以增加或减少在回放脚本期间应用的思考时间。

      例如,如果录制思考时间为4秒,则可以指示Vuser用2乘以该值,即总共为8秒。要将思考时间减少至2秒,可以用0.5乘以录制时间。

      Ø  Use random percentage of recorded think time:指定一个最小值和一个最大值,可设置Think Time值的范围,通过指定Think Time的范围,取其中的一个随机数的值来回放脚本。

      例如,如果Think Time参数为4,并且指定最小值为该值的50%,而最大值为该值的150%,则Think Time的最小值为2(50%),而最大值为6(150%)。

      Ø  Limit think time to:限制Think Time的最大值,这样VuGen在回放脚本过程中就会把脚本中大于该限制值的Think Time值用该限制值来代替。

      (3)“NetWork:Speed Simulation”标签页(如图2-58所示)

      软件测试工具

      图2-58  网络配置

      此界面为带宽的选择:选择能够最好地模拟所测试的环境的带宽,带宽越大,给Web服务器造成的压力就越大。为了方便选择带宽的大小,提供了几种选项,自上而下依次表示:

      ● Use maximum bandwidth(使用最大带宽):此项为默认选项,一般情况下运行场景不会考虑带宽大小情况,Vuser就按照网络上的最大可用带宽来运行。

      ● Use bandwidth(使用带宽):指明Vuser要模拟的特定带宽级别。如果此软件程序运行时要考虑带宽大小情况,需要规定带宽范围或者需要特定的带宽级别,就可以选择此项进行设置,可以选择从14.4K至512K bps范围内的几个带宽级别,以便模拟调制解调器、ISDN或DSL。

      ● Use custom bandwidth(使用自定义带宽):指明Vuser进行模拟的带宽限制,以位为单位指定带宽,若选择此项用户可以自己手动添加想要的带宽大小,1K=1024。

     (4)“Internet Protocol:Preferences”标签页(如图2-59所示)

      这里仅仅对两个经常需要改动的选项进行说明。

      “Checks”栏下的Enable Image and text check”:启用Image/Text检查。默认情况下此选项是没有选中的。如果在前面设置了检查点,需要先选中该项,否则运行时LoadRunner不会执行检查这个步骤。

      软件测试工具

      图2-59  启用检查点设置

       “Advanced”栏下的“Non-critical resource error as warnings”:默认选中该项,这样一些不是特别重要的资源问题(比如一个小图片)出现错误时,LoadRunner仅仅把它们当作警告,不会当作错误,至于到底哪些资源不是特别重要,请选择“Recording Option”>“Advanced”>“Non-Resources”进行设置。

      (5)“Internet Protocol:ContentCheck”标签页(如图2-60所示)

      软件测试工具

      图2-60  错误页面处理设置

      这里的设置是为了让VuGen检测何种页面为错误页面。如果被测的Web应用没有使用自定义的错误页面,那么这里不用作更改;如果被测的Web应用使用了自定义的错误页面,那么这里需要定义,以便让VuGen在运行过程中检测服务器返回的页面是否包含预定义的字符串,进而判断该页面是否为错误页面。如果是,VuGen就停止运行,指示运行失败。

      ● “Enable ContentCheck during replay”:默认选中此项,表示VuGen在回放脚本的过程中会检查页面是否包含错误信息。

      ● “New Application”:新建一类应用程序,比如ASP.NET或者JSP等。

      ● “New Rule”:在该应用下新建规则,规则中包含字符串或者字符前缀和后缀。

      ● “Set as Default”:默认情况下,当前所作的更改只适用于当前的脚本,如果想让更改适用于本机所有脚本的话,单击该按钮即可。

      ● “Import/Export”:利用该按钮可以把定义好的规则导入和导出。

      其他的标签设置采用默认值即可,这里不再详细地介绍。

501/3123>
Open Toolbar