性能测试原理及性能测试实例分析
【摘要】 在大型软件系统投入生产之前进行性能测试已经成为趋势,本文结合一个性能测试案例对性能测试的过程和原理进行了介绍。
【关键字】 性能测试 并发测试 负载测试
• 软件测试中的性能测试
软件测试是保证软件质量的重要手段,也是软件过程中一个必不可少的环节。而性能测试则隶属于软件测试中的系统级测试,它对软件在集成系统中运行的性能行为进行测试,旨在及早确定和消除软件中与构架有关的性能瓶颈。
• 性能测试的含义
目前对性能测试没有明确的定义,一般地,它主要是针对系统的性能指标制定性能测试方案,执行测试用例,得出测试结果来验证系统的性能指标是否满足既定值。性能指标里可能包括系统各个方面的能力,如系统并发处理能力,批量业务处理能力等。
• 性能测试的分解
在性能测试的执行中,可以根据具体的性能指标,分解为几种测试,根据其关系,可以在不同的时间和空间内执行。这些子测试通常包括以下几种:
并发测试:验证系统的并发处理能力。一般是和服务器端建立大量的并发连接,通过客户端的响应时间和服务器端的性能监测情况来判断系统是否达到了既定的并发能力指标。
负载测试:验证系统的负载工作能力。系统配置不变的条件下,在一定时间内,服务器端在高负载情况下的性能行为表现。这里的负载可以是用户数,交易数,事务数等。
配置测试:核实在操作条件保持不变的情况下,系统在使用不同配置时其性能行为的可接受性。
健壮性测试:核实被测系统的性能行为在异常或极端条件之下的可接受性。这里的异常或极端条件指的是资源过少,用户数过多,突发故障等。
随着软件系统的规模日益庞大,结构日趋复杂,对软件系统的性能测试已经成为必须和趋势。尤其大型的分布式软件系统更要在正式运行前进行性能测试,因为这样的系统在投入生产之后,往往要接受大批量的业务量,这对应用程序本身,操作系统, 中心数据库服务器,中间件服务器,网络设备的承受力都是一个严峻的考验。在其中任意一个环节出现的问题都可能给用户带来巨大的商业损失。预见软件系统的并发承受能力以避免商业风险,这是在软件测试阶段就应该解决的。例如中国人民银行的现代化支付系统和上海外汇交易中心的本币交易系统都在投入生产之前进行了多轮的第三方性能测试,起到了很好的作用。
下面我就介绍一个性能测试案例。
• 一个性能测试实例
• 被测系统
1)被测系统介绍
本系统应我国金融信息化发展设计,采用当今比较先进和流行的技术,是运行在城域网上的大型分布式应用系统。
本系统遵循J2EE规范,采用B/S体系结构进行设计和开发。业务主要分为交易业务和查询业务,查询业务采用J2EE规范,交易业务以J2EE体系架构为基础,进行进一步的处理,采用了TCP的四层结构。系统体系结构图如下:
图表 1被测系统体系结构设计图
- 表示层:
运行在终端上。运行java applet程序,提供协议控制和用户界面,与系统最终用户实现直接交互,通过TCP/HTTP与前置系统通讯。向前置系统发送请求报文,并接收前置系统返回的回应报文。
- 商业逻辑层:
作为中间层实现核心业务逻辑服务。
交易应用服务:运行在交易主机上。在tuxedo中间件上运行业务处理程序,按交易规则处理前置机发来的交易指令,通过tuxedo jolt与前置机连接,通过DB2 C API与数据库连接。
交易前置服务和查询前置服务:运行在前置机上。交易前置服务运行服务程序接收终端请求报文并通过tuxedo jolt客户端将其转发给交易主机,再通过轮询和同步反馈接收交易主机返回的报文,将其转发给业务终端;查询前置服务运行在weblogic应用服务器上并调用Jreport组件,通过JDBC完成对查询流指令的发送并接受数据库返回的结果给业务终端。
- 数据层:
运行在数据库主机上。负责整个系统中数据信息的存储、访问及其优化。运行DB2数据库服务程序。通过DB2 C API与交易主机通讯,JDBC与查询前置服务通讯。
数据库主机和交易主机运行在交易中心城市,前置机运行在各个分中心城市,终端是各个城市参加交易的单位,整个系统覆盖城域网。2) 被测系统的性能要求和性能指标
金融系统是业务处理十分频繁、数据交换吞吐量很大的系统,业务处理的速度直接关系到公司的经济效益和客户对公司的评价。在客观条件下,整个广域网系统必须在大业务量的情况下同时保持快速的实时响应能力,以保证整个业务系统的通畅运行。用户对此提出如下性能指标:
表格1用户要求性能指标表
下面我们会根据此系统和给定的性能指标来进行性能测试:
性能测试的目的是最大程度地模拟真实业务场景,来验证系统的性能指标,并发现可能存在的性能瓶颈。
1)对被测系统进行系统分析
我们可以看到本系统大体上由终端、前置机、交易主机、数据库主机节点组成。
在整个业务流程中,业务终端→前置机→交易主机→数据库主机形成了一个压力流串,每个节点在压力下能够正常工作是整个系统正常运转的基础。也就是说,如果其中任意一个节点在业务压力下发生了拥塞、处理不力等不正常情况,那整个系统都无法正常运转。
我们来看一下业务流程。
首先,从终端到前置机,终端产生业务报文发送至前置机,前置机上运行查询前置服务和交易前置服务,查询前置服务向下通过HTTP协议以WEB服务形式和终端连接,向上通过JDBC直接与数据库系统相连。交易前置服务向下通过基于TCP协议的socket连接和终端通讯,向上通过tuxedo jolt客户端和交易应用服务连接。交易应用服务进行业务逻辑计算,并操作数据库系统。
由以上分析,我们可以整理出整个系统的两条压力流程线来,之所以我们把其分为两条流程线,是因为交易前置服务和查询前置服务的工作原理完全不同,下与终端的连接,上与交易主机的连接也完全是独立的两个通路。
终端→交易前置机→交易主机→数据库系统
终端→查询前置机→数据库系统
下面我们先独立分析两条流程线,之后我们将再次综合分析,以考虑二者之间的相互影响作用。
第一条路线上主要运行的是登陆指令和交易指令信息。
当系统运作时,多个交易终端与交易前置服务建立socket连接,完成登陆,之后发送交易指令,造成对交易前置服务的压力。交易前置服务通过运行服务程序接收到交易指令,并检验其合法性,然后通过交易中间件tuxedo的客户端把业务的压力传递给交易主机进行处理。交易主机进行必要的金融计算和业务逻辑运行,得出反馈结果,生成消息,一方面顺原路返回到各个终端上去,一方面记录入数据库。
在本条流程线上的加压主要考验交易前置服务程序的socket多连接建立能力,tuxedo交易中间件的即时响应能力,交易主机的计算能力,以及DB2数据库的DML语句加锁机制。
第二条路线上主要运行的是查询指令信息。
查询指令产生时,通过http协议访问weblogic上的web服务器和应用服务器上的相应组件,以JDBC接口访问后台的DB2数据库,并把数据库返回的结果发送至终端界面。
在本条流程线上的加压主要验证weblogic处理能力,数据库中索引是否创建合理。
两条流程线相对独立,但又是互相依赖的。由于是对同一个数据库系统进行读操作和写操作,查询流程的结果依赖于交易流程数据的产生,交易流程的产生的数据又通过查询流程得到验证。在进行压力测试时,两者的协同会对数据库形成压力的冲击。
鉴于以上分析,结合用户性能指标,我们决定把本次性能测试分解为如下几个子测试来进行。A: 并发登陆测试:750个终端一分钟内并发登陆系统,并且响应时间在30秒之内。
B: 业务负载测试
此下又有三个子测试。
- 交易流程测试:多个终端发起交易请求,逐渐加压,以达到300笔/秒的压力为限。
- 查询流程测试:多个终端进行查询,逐渐加压,以达到400笔/秒的压力为限。查询成功与否以所请求的web页面完全展现为标准。(查询响应能力其实和数据库中的数据量有关系,后来和用户进一步确认,基础数据为30万条)
- 综合测试:
在上面两种测试都通过的情况下,进行综合测试。
2)性能测试的执行过程,性能测试依照下面的步骤来进行:
本次压力测试采用MI公司的loadrunner工具,脚本编辑和编译工作在VU Generator(脚本作坊)中进行。
理想的脚本是对现实世界的业务行为进行了完全无误的模拟,这其实是不可能的。我们的目标是使模拟的误差在我们认可的范围之内,并能有方法加以控制。
针对并发登陆测试和交易流程测试,由于两者运行机理相同,都是终端调用socket client,和交易前置的socket server建立连接,将请求消息发送至交易前置机。我们考虑采用将此部分java socket程序编入测试脚本程序,生成登陆和交易业务脚本,通过loadrunner来执行。这样做的好处是绕过终端IE界面复杂的处理逻辑,直接施压在前置机上(这种方式同时也带来了偏差,在执行测试场景时通过其它方法得到了一定的弥补)。
脚本除了要实现与前置机的socket连接,业务发送等功能,还要建立用户信息数据池,设置检测点、异常退出点,为脚本执行后的结果统计和分析提供正确的依据。
交易业务脚本内容略。部分如下:
public class Actions {/*登陆变量初始化*/ProtocolManager protocol;//ProtocolManager为实现socket连接的类 ServiceName service; //ServiceName对服务端的信息进行了封装,包括IP地址和端口号。LoginMessage login;//LoginMessage为登陆时需要向服务器发送的消息,待服务器确认并返回回应消息时,登陆成功。
protocol = new ProtocolManager(); //创建ProtocolManager类的protocol对象
service = ServiceName.getInstance();//获得ServiceName的实例
login=new LoginMessage();//创建LoginMessage类的login对象
service.setIP("200.31.10.18");//设置服务端的IP地址
service.setPort(17777);//设置服务端的端口号/*设置登陆消息*/
login.serUserName(lr.eval.string(“{loginName}”));//从数据池里读出用户名,设置在login成员变量里login.setPasswd(“1234”);//数据库中添加的用户密码都为1234/*发送登陆消息*/
protocol.login(login);//发送登陆消息
lr_start_transaction("trade");//交易开始点
TradeMessage trademessage;//生成交易消息/*设置交易消息*/
………………………….
………………………….
/*发送交易消息*/………………………….
………………………….
if(sendfail)lr_end_transaction("trade", LR_FAIL);//如果发送交易消息失败,交易结束,返回。/*循环回收主机返回的处理信息*/…………………………
…………………………
if(recievefail)lr_end_transaction("trade", LR_FAIL);//如果不能接收到主机处理回应消息,交易结束,返回。
if(recievesuccess)lr_end_transaction("trade", LR_PASS);//如果接收到主机成功处理的回应消息,交易结束,返回。
…………………………..
}在上面的例子中,我们主要对每笔交易进行了transaction化。在交易开始时设置开始检测点,交易结束时设置结束检测点,并给loadrunner报出交易状态。实际的脚本中在回收交易响应消息时还进行了拆包,在应用层上对交易状态进行识别,并非例子中只在socket层加以判断。
针对查询流程测试,由于loadrunner工具支持基于http的web访问录制功能,我们将考虑采用以录制脚本为主,手工编写脚本为辅的方法,生成查询业务脚本,通过loadrunner来执行。由于查询脚本基本由录制生成http请求和应答,不同的压力测试工具录制会有差别,这里就不再写出查询脚本样例。
在本次性能测试中,用户提出的性能指标不够细致和确切,通过对用户调查和实际业务分析,我们把性能指标的实现方式进行了明确的定位。A:并发登陆测试场景
并发登陆750用户/分钟,登陆响应时间在30秒之内。仔细考虑一下,这里的并发登陆750用户/分钟指的是系统能够在1分钟内接受750个用户的登陆请求,而处理的效果如何则在交易终端体现,即登陆响应时间。基于这样的理解,我们把用户性能指标转化为如下的测试场景:
从第一秒钟开始,用loadrunner每秒钟登陆13个用户,并保持socket连接,直到1分钟结束,从终端向系统一共发送750个左右的用户登陆请求,系统在一分钟内建立了750个连接。在终端观察并统计登陆响应时间。如果系统不能响应持续增加的登陆请求或平均登陆响应时间大于30秒,并发登陆测试场景都不能算通过。
为了帮助用户更加深入了解系统的能力,我们对系统的瞬时并发能力进行测试,即测试系统所能承受的最大的瞬时并发用户登陆连接请求个数。这个场景通过loadrunner在登陆前设置同步点来实现,这个结果将结合上一个结果一同反映系统的登陆处理能力。B:交易流程测试和查询流程测试:
在这里我们只对系统的业务负载能力做测试(并发处理能力在登陆测试中已经得到考证)。测试场景如下:
在loadrunner中,建立goal-orented的测试场景,以400笔/秒为目标,将调度权交给loadrunner来试图达到这个指标。
C: 综合测试:
交易流程测试和查询流程测试同时进行。
以上的测试场景要求均可在loadrunner中的Controller进行设置完成。
测试场景的创建之后,我们的测试任务更加具体化和清晰化。
在loadrunner中的controller中开启unix系统资源计数器,weblogic计数器,DB2计数器,检测系统资源消耗情况,并最终和测试结果数据合并,成为分析图表。
测试结果可在测试执行完毕后,通过loadrunner工具中的Analysis(分析器)获得。
A: 并发登陆测试
依照设计好的测试场景,用loadrunner工具在一分钟内渐增向系统发送登陆请求。分别进行三次,结果如下
表格 2登陆测试结果数据表
注:这里的登陆成功用户指的是系统接受了登陆请求,并建立了连接。平均响应时间在登陆脚本里设置检测点,由loadrunner工具自动获得。
考察系统的瞬时并发处理能力:在完成上一步测试的前提下,逐步增加瞬时并发登陆用户数,直到系统极限。
测试执行结果如下:
B: 负载测试
- 交易流程测试:
测试结果如下:
对于通过网络接口发送的批量业务请求,均在性能指标所指定的时间范围内得到请求成功的反馈消息,说明主机已经处理成功。
在通过网络接口发送业务请求的同时,开启IE,通过实际终端界面进行登陆和交易,系统响应时间延长,界面显示和刷新明显变慢,到业务量高峰时期,界面已经不能显示任何信息,处于不可工作的状态。
需求说明的是系统正常工作时,每个界面终端不仅应该能够展示己方的交易信息,还要展示其他交易单位的交易信息和系统信息。因此当交易量大的时候,界面需要展示的信息量是巨大的,这本身对终端界面是一个性能考验。
- 查询流程测试:
本流程测试在交易流程测试之后进行,以利用其生成的数据。
测试结果基本满足性能指标。
- 综合测试
由于交易流程测试的未通过,本测试已经不能执行。
A:并发测试结果分析
根据上述的并发测试响应时间表,我们可以得出以下的结论:
被测系统在一分钟内并不能接受750个用户的登陆请求,其可接受的登陆请求用户数大概为490个左右。在这样的条件下,登陆响应时间在用户要求范围之内。
被测系统的瞬时并发处理能力约为122个用户。
B: 交易流程测试结果分析及性能评价
根据交易流程测试结果可知,通过脚本程序进行业务行为,发送业务请求消息到回收主机处理回应消息,这段时间系统是顺畅的,反应也是迅速的,但是在终端界面却不能即时展现消息。这说明信息的回馈通路在终端界面出现了性能瓶颈。当界面需要在短时间内展示大量交易信息时,已经不能承受负荷。这与终端采用java applet技术有关。
C: 查询流程测试结果分析
查询流程基本符合性能指标。
需要说明的是,实际中,以上每个场景的测试都执行了多次,中间件参数进行了多次的调优。从以上测试的结果分析也可以看出,我们的性能测试瓶颈不是出现在中间件产品上,而是在自身开发的程序上。
• 总结
由以上的实例过程我们可以看出性能测试基本由以下几个步骤进行
- 系统分析
将系统的性能指标转化为性能测试的具体目标。通常在这一步骤里,要分析被测系统结构,结合性能指标,制定具体的性能测试实施方案。这要求测试人员对被测系统结构和实施业务的全面掌握。
2. 建立虚拟用户脚本
将业务流程转化为测试脚本,通常指的是虚拟用户脚本或虚拟用户。虚拟用户通过驱动一个真正的客户程序来模拟真实用户。在这一步骤里,要将各类被测业务流程从头至尾进行确认和记录,弄清这些交易过程可以帮助分析到每步操作的细节和时间,并能精确地转化为脚本。此过程类似制造一个能够模仿人的行为和动作的机器人过程。这个步骤非常重要,在这里将现实世界中的单个用户行为比较精确地转化为计算机程序语言。如果对现实世界的行为模仿失真,不能反映真实世界,性能测试的有效性和必要性也就失去了意义。
3. 根据用户性能指标创建测试场景
根据真实业务场景,将单个用户的行为进行复制和控制,转化为多个用户的行为。在这个步骤里,对脚本的执行制定规则和约束关系。具体涉及到交易量,并发时序等参数的设置。这好比是指挥脚本运行的司令部。这个步骤十分关键,往往需要结合用户性能指标进行细致地分析。
4. 运行测试场景,同步监测应用性能
在性能测试运行中,实时监测能让测试人员在测试过程中的任何时刻都可以了解应用程序的性能优劣。系统的每一部件都需要监测:客户端,网络,web服务器,应用服务器,数据库和所有服务器硬件。实时监测可以在测试执行中及早发现性能瓶颈。
5. 性能测试的结果分析和性能评价
结合测试结果数据,分析出系统性能行为表现的规律,并准确定位系统的性能瓶颈所在。在这个步骤里,可以利用数学手段对大批量数据进行计算和统计,使结果更加具有客观性。在性能测试中,需要注意的是,能够执行的性能测试方案并不一定是成功的,成败的关键在于其是否精确地对真实世界进行了模拟。
在整个性能测试过程中,自动化测试工具的选择只能影响性能测试执行的复杂程度,简便一些或繁杂一些;但人的分析和思考却会直接导致性能测试的成败。所以本篇着重于对性能测试思路的整理。测试工具的介绍可以参看有关压力测试工具的资料。
注1:在本次性能测试案例中,还涉及到健壮性测试和可恢复性测试,限于篇幅,只介绍了并发测试和负载测试。
注2:loadrunner脚本样例并非实际运行脚本,只是为了表示其流程。