您的当前位置:首页正文

LoadRunner性能测试实战

来源:帮我找美食网
LoadRunner性能测试实战

内容介绍:

很多使用LoadRunner的测试人员经常面临两个难题:脚本开发与性能测试分析。本书就是基于帮助测试人员解决这两个问题而编写,致力于使读者学精LoadRunnner这一强大的性能测试工具。

全书共分为四部分:入门篇、基础篇、探索篇、实战篇。第一篇入门篇的内容包括第1章和第2章,着重于讲解性能测试与LoadRunner的基础理论知识。第二篇基础篇的内容包括第3章至第5章,是LoadRunner的基本使用部分,着重讲解Virtual User Generator、Controller、Analysis的使用方法。第三篇探索篇的...

第1部分 入门篇............................................................................................... 1 第1章 性能测试基础知识.. 3 1.1 性能测试基本概念... 4 1.1.1 什么是性能测试... 4 1.1.2 性能测试应用领域... 6 1.1.3 性能测试常见术语... 8 1.2 全面性能测试模型... 11 1.2.1 性能测试策略模型... 14 1.2.2 性能测试用例模型... 17 1.2.3 模型的使用方法... 20 1.3 性能测试调整基础... 21 1.4 如何做好性能测试... 24 1.5 本章小结... 28

第2章 LoadRunner基础知识.. 29 2.1 LoadRunner简介... 29 2.1.1 LoadRunner主要特点... 29 2.1.2 LoadRunner常用术语... 31 2.2 LoadRunner工作原理... 32 2.3 LoadRunner测试流程... 33 2.4 LoadRunner的部署与安装... 35 2.5 本章小结... 41

第2部分 基础篇............................................................................................. 43 第3章 脚本的录制与开发.. 45 3.1 Virtual User Generator简介... 45

3.1.1 VuGen录制原理... 46 3.1.2 VuGen功能简介... 48 3.1.3 如何选择协议... 49 3.2 VuGen录制功能详解... 50 3.2.1 录制参数设置... 50 3.2.2 脚本录制与创建事务... 57 3.2.3 回放与调试脚本... 61 3.2.4 脚本录制的基本原则... 63 3.3 修改虚拟用户脚本... 64 3.3.1 参数化功能... 64 3.3.2 深入集合点... 71 3.3.3 巧用检查点... 72 3.3.4 关联... 78

3.4 配置虚拟用户脚本... 80 3.5 两个常用函数介绍... 84 3.6 本章小结... 86

第4章 场景的创建与执行.. 87 4.1 Controller简介... 87 4.2 场景类型介绍... 88 4.2.1 手动测试场景... 88 4.2.2 面向目标的测试场景... 90 4.3 测试场景设计... 93 4.3.1 配置测试脚本... 93 4.3.2 配置Generator 94 4.3.3 配置Schedule. 95 4.3.4 集合点配置... 99 4.3.5 IP Spoofer配置... 100 4.3.6 其他设置场景... 106

4.4 执行测试场景... 108 4.4.1 启动测试场景... 108 4.4.2 控制用户与用户组... 108 4.4.3 查看场景与用户状态... 109 4.4.4 控制集合点... 110 4.4.5 查看运行数据图... 110 4.5 监控系统资源... 111

4.5.1 监控Windows系统资源... 112 4.5.2 监控Linux/Unix系统资源... 114 4.6 本章小结... 121

第5章 性能测试结果分析.. 123 5.1 如何分析性能测试结果... 124 5.1.1 性能分析基础知识... 125 5.1.2 Analysis使用基础... 127 5.1.3 一个视频网站例子... 135 5.2 如何从分析图中发现问题... 148 5.2.1 虚拟用户图... 148 5.2.2 事务图... 151 5.2.3 Web资源图... 160 5.2.4 网页细分图... 166 5.2.5 小结... 179

5.3 分析图的处理方法... 179 5.3.1 修改默认配置... 180 5.3.2 合并分析图... 187 5.3.3 自动关联... 188 5.3.4 场景运行比较... 191 5.4 Analysis分析报告... 193

5.4.1 事务活动报告(Activity Reports)... 193

5.4.2 事务性能报告(Performance Reports)... 196 5.4.3 HTML与Word报告... 199 5.5 本章小结... 206

第3部分 探索篇.......................................................................................... 209 第6章 用Visual C++增强虚拟用户.. 211

6.1 认识LoadRunner动态链接库的调用功能... 211 6.1.1 动态链接库调用功能简介... 211 6.1.2 动态链接库调用功能适用范围... 212 6.2 创建与调用动态链接库... 212 6.2.1 用Visual C++创建Dll 212 6.2.2 Dll调用方法... 215 6.2.3 载入头文件方法... 217 6.2.4 Dll调用需注意的问题... 220 6.3 UDP发包应用案例... 222 6.3.1 测试内容简介... 222 6.3.2 测试程序设计... 222 6.3.3 虚拟用户脚本... 223 6.3.4 测试场景设置... 224 6.3.5 测试结果分析... 225 6.4 本章小结... 226

第7章 深入Java虚拟用户.. 227 7.1 认识Java虚拟用户... 227 7.1.1 Java虚拟用户协议... 227 7.1.2 Java虚拟用户适用范围... 230 7.1.3 脚本开发环境配置... 231 7.2 Java脚本开发基础... 234 7.2.1 Java虚拟用户开发基础... 234 7.2.2 LoadRunner的Java API. 243

7.3 Java算法测试案例... 245 7.4 本章小结... 260

第8章 深入.NET虚拟用户.. 261 8.1 认识.NET虚拟用户... 261 8.1.1 .NET虚拟用户适用范围... 261 8.1.2 安装与配置.NET插件... 262 8.2 创建.NET虚拟用户... 264 8.2.1 创建虚拟用户项目... 264 8.2.2 参数、集合点、事务... 266 8.3 网站视频性能测试应用案例... 271 8.3.1 创建自定义的播放器类... 272 8.3.2 创建抽象虚拟用户类... 276 8.3.3 创建抽象并发测试类... 282 8.3.4 创建自定义虚拟用户脚本... 284 8.3.5 创建LoadRunner .NET虚拟用户... 287 8.3.6 案例总结... 290 8.4 本章小结... 290

第9章 LoadRunner特殊协议应用.. 291 9.1 Windows Sockets协议应用... 291 9.1.1 录制Windows Sockets协议脚本... 292 9.1.2 增强Windows Sockets协议脚本... 294 9.2 WAP协议应用... 298

9.3 Web Services协议应用... 302 9.3.1 Web Services协议简介... 302 9.3.2 录制Web Services协议脚本... 303 9.4 FTP协议应用... 312 9.5 本章小结... 317

第4部分 实战篇.......................................................................................... 319

第10章 电子商务平台测试案例.. 321 10.1 GBE测试项目简介... 321 10.1.1 项目背景信息... 321 10.1.2 系统功能简介... 322 10.1.3 项目测试计划... 323 10.2 性能测试规划与设计... 323

10.2.1 性能测试的种类、范围、目标... 324 10.2.2 人力资源、进度安排... 325 10.2.3 测试环境需求... 325 10.2.4 选择测试工具... 327 10.2.5 用户场景分析与设计... 328 10.2.6 性能测试计划... 333 10.2.7 测试用例设计... 334 10.2.8 其他事项... 341 10.3 性能测试准备... 341 10.3.1 测试环境... 341 10.3.2 系统使用培训... 342 10.3.3 测试数据... 343 10.3.4 虚拟用户脚本... 346 10.4 测试的实施与控制... 349 10.4.1 设计测试用例场景... 349 10.4.2 执行测试用例场景... 351 10.4.3 进度与变更控制... 359 10.5 测试结论与建议... 360 10.5.1 测试结果综述.... 360 10.5.2 系统性能优化建议.... 361 10.5.3 风险分析... 362 10.6 本章小结... 362

附录A LoadRunner性能测试常见问题.. 365 附录B LoadRunner性能测试模板.. 373 B.1 性能测试计划模板... 373 B.1.1 项目背景简介... 373 B.1.2 测试方案简介... 373 B.1.3 测试环境与资源... 373 B.1.4 项目里程碑... 374 B.1.5 技能培训计划... 374 B.1.6 风险分析... 374 B.1.7 计划结束标准... 374 B.2 性能测试用例模板... 374 B.2.1文档介绍... 374 B.2.2 测试需求分析... 375 B.2.3 性能测试用例... 375 B.3 性能测试报告模板... 380 B.3.1 基本信息... 380 B.3.2 测试环境描述... 381 B.3.3 性能测试用例执行分析... 381 B.3.4 测试结果综合分析及建议... 381 B.3.5 测试经验总结... 381 后 记.. 383

前言

在作者的另一作品《Web性能测试实战》中,曾经提到过“软件亚健康”这个概念。现在,亚健康不但威胁着IT人的生活质量,也威胁很多应用软件的性能。为此,在《Web性能测试实战》一书中,作者提出了“全面性能测试模型”,期望能够成为解决软件亚健康问题的一剂“良药”。

“全面性能测试模型”包含了测试策略制定、测试用例设计、模型使用方法三部分内容,基本覆盖了性能测试规划和设计的相关内容,为开展性能测试提供了一种可行的方案。借助本模型,软件开发和测试人员可以更好的组织与规划性能测试,避免在项目后期遭遇性能问题的被动局面。

不过要想做好性能测试,仅有性能测试模型还是远远不够的,因为还缺少像LoadRunner这样令性能测试工作如虎添翼的性能测试利器。本书将和读者一起深入LoadRunner的性能测试世界,探讨在企业的性能测试项目中如何应用它来发现应用系统存在的性能问题。 LoadRunner在性能测试中的地位

对于很多使用LoadRunner的测试人员而言,性能测试工作中最大的障碍就是测试脚本开发与测试结果分析,这导致很多测试人员忽略了测试规划与设计的重要性,反而认为能开发测试脚本、运行测试场景、分析测试结果就算做好性能测试了。

要想做好性能测试,首先应该把重心放在测试的规划与设计上,尤其要注重测试用例的设计,仅仅能写测试程序与运行测试脚本是远远不够的。诸如LoadRunner等测试工具仅仅是性能测试的执行与分析工具,它们应该服从于测试设计人员的意志。测试工具的使用属于测试人员的基本功,应该在开展性能测试工作前修炼好。只有好的测试用例或者测试场景才能发现系统的问题,这才是性能测试的本质所在。

性能测试分析同样依赖于前面工作的输出结果,不是随便一个测试结果就能发现问题的。所谓“万丈高楼平地起”,性能分析的准确性同样取决于此前所做的设计与实施等“地基”是否可靠。可以说,性能测试分析仅仅是百米赛跑的最后二十米而已。当然,这并不是说性能测试分析不重要,因为“最后冲刺的二十米没有跑好”,前面工作做的再好也是徒劳的。因此不难理解,性能测试分析工作开展的根基就是前面测试场景执行的结果。要想保证性能测试分析的结论是正确的,则测试结果数据首先就应该是正确的,而这也意味着测试场景以及测试执行过程都应该是正确的。

实际上,性能测试从始至终都应该是相当严谨的一项工程,各个阶段的工作环环相扣,性能测试工程师应该认真对待各个阶段的工作。如果一味地追求找出系统瓶颈,无疑是舍本逐末的做法。

因此,在性能测试工作中首先要做好性能测试的规划与设计工作,然后再借助LoadRunner的强大功能来发现系统存在的问题。 如何通过本书学习LoadRunner

首先应该弄清楚学习LoadRunner的目的,那就是在项目的性能测试中应用LoadRunner来发现系统的性能问题。因此,仅仅会用LoadRunner还远远不够,这也是为什么很多培训班出来的学员虽然把工具用的非常熟练,但是仍然不能做好性能测试工作。

学好LoadRunner的标准是真正能够把LoadRunner应用到实际项目中去,这就要求学习LoadRunner的同时一定要学好性能测试相关知识。本书的第1章即为基本的性能测试知识,读者需要认真体会这些内容,建议在学习后面的内容时,经常翻阅本章的内容。如果要学习更多的性能测试规划与设计的知识以及性能测试案例,建议读者参考本书的姊妹篇《Web性能测试实战》。

本书的第2章是LoadRunner的简介部分,读者需要通过本章了解LoadRunner的工作原理、测试流程、部署与安装等内容,尤其要掌握图2-1所示的LoadRunner工作原理,这是用LoadRunner开展工作的基础。

本书的第3章、第4章、第5章分别讲解了LoadRunner的Virtual User Generator、Controller、Analysis。这三大组件分别负责脚本的录制与开发、场景的创建与执行、测试结果分析工作。用LoadRunner来开展性能测试,必须要掌握这三大组件的使用。如果连基本的工具都没有用好,很难正确地执行设计好的测试用例,更不用说根据结果来分析系统的瓶颈了。在第3~5章中,详细探讨了LoadRunner各个组件的使用细节,但是这还远远不够,尤其对于那些只会录制或者简单修改录制结果的测试人员!学习这三章的内容时,最好的方法是结合LoadRunner的联机帮助文档,这样可以学习到更多的内容。

学习完第3~5章后,可能还有一些读者会问:“我还是不会自己写测试脚本,很多协议仍然不能进行测试怎么办?”碰到这种情况就需要补习自己的开发知识了。

开发知识应该分两个方面来学习:一是面向对象基础知识的学习,二是开发语言的学习。很多人可能会认为面向对象基础知识比较通用,相对容易学习;而开发语言种类繁多,不知

道如何入手。根据作者的经验,这两个方面应该结合起来进行:面向对象是现在主流开发语言的灵魂,一起学习可以互相促进。具体做法就是选择C++、Java、C#等一种主流语言来学习,只要这门语言是自己所在公司的主流语言即可。当学会面向对象基础和一门语言后,再去学习其它的语言将会非常容易。

具有一定的开发能力后,就可以开始本书探索篇第6~9章的学习。这四章是LoadRunner的探索篇,讲解了在LoadRunner中如何应用C++、Java、C#语言进行开发以及一些特殊的脚本协议。

相信通过前面9章的学习,读者已经掌握LoadRunner的精髓了。不过本书不是一本“LoadRunner使用百科大全”,接下来就需要读者自己不断地应用与探索LoadRunner了,逐步完成成为一个LoadRunner高手的蜕变过程。 如何学习本书的性能测试案例

本书在第10章中,花了很大的篇幅介绍了一个电子商务平台的性能测试案例,目的不是为了介绍如何测试电子商务系统,而是让读者在掌握前面技能的基础上,更加深入地体会在项目中如何通过LoadRunner来实施性能测试。因此,案例的业务并不重要,读者也没有必要深究具体的细节。通过本案例,能清晰地了解了能测试的整个过程就已经达到了目的。

本书案例的学习重点在以下几个方面:

l 借助案例体会“全面性能测试模型”在GBE项目中的应用;

l 学习性能测试规划与设计中的需求分析过程,例如测试环境需求、人力资源; l 学习性能测试规划与设计中的测试场景分析与设计、测试用例设计; l 学习如何做好性能测试实施前的准备工作; l 测试执行过程的进度与变更控制; l 一些分析性能问题的过程。

关于性能测试案例更多的内容,读者可以阅读《Web性能测试实战》中的案例部分。 关于本书

本书的主旨在于让读者学会LoadRunner的应用,并能在此基础上自行探索性能测试世界。

本书共分为四部分:入门篇、基础篇、探索篇、实战篇。

第一部分:入门篇,包括第1章和第2章,着重于讲解性能测试与LoadRunner的基础理论知识。在第1章中,讲解了性能测试基本概念、全面性能测试模型、性能测试调整等基础的性能测试理论知识;第2章则介绍了LoadRunner的特点与术语、工作原理、测试流程、部署与安装等内容。

第二部分:基础篇,包括第3章至第5章,着重讲解LoadRunner三大组件的使用,是LoadRunner的基本使用部分。在第3章中,主要讲解如何在Virtual User Generator中完成代码的录制与开发;第4章讲解如何在Controller中创建与执行场景;第5章中讲解如何结合Analysis来分析性能测试结果。

第三部分:探索篇,包括第6章至第9章,着重讲解LoadRunner的高级应用。第6章讲解如何用Visual C++来增强虚拟用户;第7章深入探索了Java虚拟用户;第8章深入探索了.NET虚拟用户;第9章则讲解了Socket虚拟用户的相关知识。

第四部分:实战篇,即第10章,结合案例来讲解在具体项目中如何应用LoadRunner来完成性能测试工作。在第10章中,通过真实的性能测试实例,向读者展示了如何在项目中完成性能测试的整体规划与设计、测试的准备与实施、测试结果分析等工作。 致谢

感谢广大读者对《Web性能测试实战》一书的支持,读者的支持是作者写作的真正动力。正是一年来因为大家对《Web性能测试实战》的肯定才促使我完成本书的写作工作;

感谢博文视点周筠老师对本书的支持,周老师对我这个新人一直给予很大的鼓励; 感谢电子工业出版社博文视点资讯有限公司的陈元玉编辑,她是本书的责任编辑; 感谢师兄王玉亭,他再次为本书提供了很多素材;

感谢同事关晓培、周雪松、李熠,他们为本书提供了很多素材; 感谢电子工业出版社为本书辛勤付出的所有朋友们;

特别感谢夫人小姬,她通篇审校了本书并润色了那些难于理解的句子,特别是她对我在公司的日常工作和编写工作的支持,因为本书占据了大量可以陪她的时间;

最后要感谢自己的父母和老师,能写出本书是父母和老师多年教育的结果。

软件在性能方面的“亚健康”问题一直伴随着国内很多企业的软件产品而存在。早期由于多数软件应用系统在企业中得不到有效的推广应用,因此用户往往会忽略自己在性能方面的需求。而现在软件几乎渗透到人们工作与生活的各个方面,因而软件的性能开始得到越来越多的重视。

随着软件工程技术、软件开发方法和软件开发工具的发展,一方面使人们可以快速开发更加复杂的应用,另一方面也使开发出的软件规模越来越庞大,架构越来越复杂。随之而来的是软件性能问题也越来越多,最终导致很多软件系统由于性能方面存在问题而停止使用,给软件公司以及客户都带来了一定的损失。因此,解决软件性能问题是十分必要的一项工作中,对于企业自身以及客户都具有重要的现实意义。

在绍英的上一本著作《Web性能测试实战》中,为接近软件性能问题提出了“全面性能测试模型”,以期成为解决软件亚健康问题的一剂良药。“全面性能测试模型”包含了性能测试策略制定、测试用例设计、模型使用方法三部分内容,覆盖了性能测试规划和设计的相关内容,为开展性能测试工作提供了一种可行的方案。但是仅有理论是不够的,对于性能测试工作而言,不但需要好的性能测试理论作为工作指导,更需要掌握好的性能测试工具,因此本书的几位作者共同创作了《LoadRunner性能测试实战》一书。

LoadRunner是目前国内性能测试领域应用最广泛的工具之一,它可以通过模拟成千上万的用户,很快地帮助用户确认和查找性能问题。但是国内图书市场上却没有任何相关书籍,《LoadRunner性能测试实战》填补了这个空白。

《LoadRunner性能测试实战》是非常注重实际应用的作品。书中详细描述了LoadRunner在性能测试领域诸多方面的应用,并结合具体的案例来说明如何应用《Web性能测试实战》一书中提到的“全面性能测试模型”。强大的性能测试工具加上合理的理论来指导,将为读者打开很多新的思路。

本书是由三位作者共同完成的。绍英有流媒体、P2P、电子政务、银行、门户网站等领域应用软件的性能测试经验,在LoadRunner方面更有五年以上的使用经验。他曾到很多公司去推广自己的性能测试模型以及讲解LoadRunner课程,对企业在软件测试方面的需求非常熟悉;建华是在读研究生,因此有充裕的时间

来研究LoadRunner的特殊应用;小姬在性能测试方面也有着丰富的经验。相信他们的这些实践经验是很多测试人员急需的。

本书对国内软件企业提高性能测试水平是很有价值的。我很高兴能为这本实战性非常强的作品做序,预祝《LoadRunner性能测试实战》早日出版。也希望国内有更多的人来关注软件性能测试,探讨解决软件亚健康问题的方法!

北京大学软件与微电子学院副教授

北京市软件促进中心专家顾问 黎怡兰

(Melody Le)

1.1 性能测试基本概念

在一些软件项目中,项目经理或测试经理经常会安排测试工程师进行下面的工作: l 用LoadRunner测试系统的最大并发用户数。 l 用LoadRunner测试系统8小时的最大业务吞吐量。 l 用LoadRunner测试系统的稳定性与健壮性。

l 用LoadRunner测试系统在数据达到100万条记录时的性能。 l 用LoadRunner测试核心事务响应时间是否满足用户的需求。

可以说,现在很多IT企业的性能测试工作已经离不开LoadRunner了。不过,尽管使用了LoadRunner这一强大的工具,很多企业软件产品遇到的性能问题仍未能解决——因为仅有好的测试工具是不够的。除了比较实用的测试工具外,要想做好性能测试还应该掌握相关的理论知识。只有以坚实的理论作为实际工作的依托,才能让测试工具发挥出应有的功效。

本章将介绍一些性能测试的基础知识,主要内容如下:

n 性能测试基本概念 n 全面性能测试模型 n 性能测试调整基础 n 如何做好性能测试

提示:关于性能测试理论的更多内容,可以参考作者性能测试方面的专著《Web

性能测试实战》,电子工业出版社,2006年5月出版。

1.1 性能测试基本概念

在软件系统日益复杂的今天,性能已经成为软件质量重要的衡量标准之一,这一点尤其体现在和Web相关的系统上。软件几乎无处不在,在给用户带来方便的同时,也对开发人员和测试人员提出了更高的要求。性能测试不但要求测试人员具备很强的技术能力,还要具备综合分析问题的能力。本节从性能测试的概念入手,强化性能测试的基础知识。 1.1.1 什么是性能测试

目前很少能见到性能测试的准确定义,但是性能测试又似乎是涉及范围非常广泛的测试。压力测试、负载测试、强度测试、稳定性测试、健壮性测试、大数据量测试……都和性能测试有着密切的关系。

在本书中,主要从狭义和广义两方面来讨论性能测试。

狭义的性能测试主要用于描述常规的性能测试,是指通过模拟生产运行的业务压力或用户使用场景来测试系统的性能是否满足生产性能的要求。

例如,以实际投产环境进行测试,来求出最大的吞吐量与最佳响应时间,以保证上线的平稳、安全等。性能测试是一种“正常”的测试,主要测试正常使用时系统是否满足要求,同时可能为了保留系统的扩展空间而进行的一些稍稍超出“正常”范围的测试。

广义的性能测试则是压力测试、负载测试、强度测试、并发(用户)测试、大数据量测试、配置测试、可靠性测试等和性能相关的测试统称。下面分别介绍各类测试的主要内容和特点。 压力测试

对系统不断施加压力的测试,是通过确定一个系统的瓶颈或不能接收用户请求的性能点,来获得系统能提供的最大服务级别的测试。例如测试一个Web站点在大量的负荷下,系统的事务响应时间何时会变得不可接受或事务不能正常执行。

压力测试的目的是发现在什么条件下系统的性能变得不可接受,并通过对应用程序施加越来越大的负载,直到发现应用程序性能下降的拐点。压力测试和负载测试有些类似,但是通常把负载测试描述成一种特定类型的压力测试——例如增加用户数量或延长压力时间以对应用程序进行压力测试。

负载测试

对系统不断地增加压力或增加一定压力下的持续时间,直到系统的一些性能指标达到极限,例如响应时间超过预定指标或某种资源已经达到饱和状态。这种测试可以找到系统的处理极限,为系统调优提供依据。

压力测试侧重压力大小,而负载测试往往强调压力持续的时间。在实际工作中,没有必要严格区分这两个概念,有关内容可以参见后面1.2节的“全面性能测试模型”。 强度测试

强度测试主要是为了检查程序对异常情况的抵抗能力。强度测试总是迫使系统在异常的资源配置下运行。例如:

l 当正常的用户点击率为“1000次/秒”时,运行点击率为“2000次/秒”的测试用例; l 运行需要最大存储空间(或其他资源)的测试用例;

l 运行可能导致操作系统崩溃或磁盘数据剧烈抖动的测试用例,等等。

强度测试是一种特别重要的测试,对测试系统的稳定性,以及系统未来的扩展空间均具有重要的意义。在这种异常条件下进行的测试,更容易发现系统是否稳定以及性能方面是否容易扩展。

疲劳强度测试是一类特殊的强度测试,主要测试系统长时间运行后的性能表现,例如7×24小时的压力测试。 并发(用户)测试

主要指当测试多个用户并同时访问同一个应用程序、同一个模块或数据记录时是否存在死锁或其他性能问题,几乎所有的性能测试都会涉及并发测试。在具体的性能测试工作中,并发用户往往都是借助工具来进行模拟的,LoadRunner中称之为并发虚拟用户。 大数据量测试

大数据量测试分为两种:一种是针对某些系统存储、传输、统计查询等业务进行大数据量的测试;另一种是与并发测试相结合的极限状态下的综合数据测试。如专项的大数据量测试主要针对前者,后者尽量放在并发测试中。此外,也可以把大数据量测试分为“运行时大数据量测试”与“历史大数据量测试”来进行测试用例设计。 配置测试

配置测试主要指通过测试找到系统各项资源的最优分配原则。配置测试是系统调优的重要依据。例如,可以通过不停地调整Oracle的内存参数来进行测试,使之达到一个较好的性能。

可以看出,配置测试本质上是前面提到的某些种类的性能测试组合在一起而进行的测试。 可靠性测试

在给系统加载一定业务压力的情况下,使系统运行一段时间,以此检测系统是否稳定。例如,可以施加让CPU资源保持70%~90%使用率的压力,连续对系统加压8个小时,然后根据结果分析系统是否稳定。

这么多类型的性能测试看起来很吓人,实际上它们大多是密切相关的。例如,运行8个小时来测试系统是否可靠,而这个测试极有可能包含了可靠性测试、强度测试、并发(用户)测试、负载测试,等等。

因此,当实施性能测试时绝不能割裂它们的内部联系去进行,而应分析它们之间的关系,以一种高效的方式来规划与设计性能测试。为此,本书在1.2节提出了“全面性能测试模型”,以更好的方式来开展性能测试工作。 1.1.2 性能测试应用领域

性能测试往往是为了实现下面的一个或几个目标: l 判定软件是否满足预期的性能需求; l 根据测试结果判定软件的性能表现;

l 查找系统可能存在的性能问题,如果有,则找出并加以解决; l 发现一些应用程序在功能实现方面的缺陷; l 对一些存在性能问题的系统,找出瓶颈并加以解决; l 为用户部署系统提供性能参考; l ……

通过分析性能测试的种种目标,不难总结出性能测试主要应用在几个领域中,下面分别予以介绍。 系统的性能瓶颈定位

系统的性能瓶颈定位是性能测试最常见的应用领域。借助LoadRunner等工具,可以在测试场景运行过程中监控系统资源、Web服务器资源等运行数据,与响应时间进行同步分析,可以在一定程度上进行性能瓶颈的分析与定位。 系统的参数配置

通过性能测试可以测试系统在不同参数配置下的性能表现,进而找出令系统表现更优的系统配置参数,为应用系统投产提供最佳配置建议。

实际上,操作系统、数据库、中间件服务器等的参数配置是应用系统发生性能问题的重要原因。

例如分配给Oracle的内存大小与系统自身的业务特点有极大关系,配置不同的数据库,性能表现就会不同;而即使在内存一定的情况下,SGA的分配也会对性能产生很大的影响。因此,要通过测试,以确定内存的最佳配置。 发现一些软件算法方面的缺陷

一些多线程、同步并发算法在单用户模式下测试是很难发现问题的,只有通过模拟多用户的并发操作,才能验证其运行是否正常与稳定。

例如作者就经历过在一次性能测试过程中,测试人员模拟多个用户并发时发现的一个明显的缺陷:测试对象是一个随机分配任务的模块,只要有用户申请,就会给用户分配任务。这个算法在单用户情况下调试没有任何问题,但是当多个用户同时申请任务时,就发生了把同一任务分配给多个不同用户的现象。经证实,这个缺陷就是“同步算法”发生了问题而引起的。

系统的验收测试

系统验收测试经常会验证一些预期的性能指标,或者验证系统中一些事务指标是否符合用户期望,这时就需要借助性能测试来完成验证工作。

随着用户对性能的重视,现在性能测试几乎是系统验收测试中必不可少的内容之一。用户甚至自己进行专门的性能测试来验证系统上线前的性能,以保证运行时的性能稳定。因此,性能测试在用户验收测试中越来越重要。 系统容量规划

通过总结系统在不同硬件环境下的性能表现,可以为系统部署时提供非常好的参考。对于一些性能要求较高的系统,性能测试可以为硬件规划提供很好的参考数据,使用户在购买硬件时“有据可依”。例如同一系列机型:两颗CPU系统支持500用户并发、四颗CPU支持800用户并发,这些都是用户根据自身需求来规划硬件的重要依据。 产品评估/选型

产品评估/选型是性能测试的又一应用领域。通过性能测试,可以对产品的软硬件性能进行更全面的评估,选出更适合自己的产品类型。

性能测试的应用领域越来越广,因此在实际工作中,性能测试往往会一次应用在一个或多个领域。对于软件性能测试设计人员,应该根据测试的具体应用领域、测试原则和目标来进行性能测试的规划与设计。 1.1.3 性能测试常见术语

本节将介绍一些性能测试中的常见术语,只有掌握这些基础的性能测试知识,才可以进一步开展测试工作。性能测试常见的术语主要有并发、并发用户数量、请求响应时间、事务响应时间、吞吐量、吞吐率、TPS、点击率、资源利用率等,下面分别介绍它们。

并发

狭义的并发一般分两种情况。一种是严格意义上的并发,即所有的用户在同一时刻做同一件事情或操作,这种操作一般针对同一类型的业务。例如在信用卡审批业务中,一定数目的用户在同一时刻对已经完成的审批业务进行提交(操作的不是同一记录);还有一种是特例,即所有用户进行完全一样的操作,目的是测试数据库和程序对并发操作的处理。例如在信用卡审批业务中,所有的用户可以一起申请业务,或者修改同一条记录。

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

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

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

并发主要针对服务器而言,是否并发的关键是看用户的操作是否对服务器产生了影响。因此,并发用户数量的正确理解是,在同一时刻与服务器进行交互的在线用户数量。这些用户的最大特征是和服务器发生了交互,这种交互既可以是单向传送数据的,也可以是双向传送数据的。

并发用户数量的统计方法目前还没有准确的公式,因为不同的系统会有不同的并发特点。例如OA系统统计并发用户数量的经验公式为:使用系统的用户数量×(5%~20%)。对于这个公式,没有必要拘泥于计算出的结果,因为为了保证系统的扩展空间,测试时的并

发用户数量都会稍稍大一些,除非要测试系统能承受的最大并发用户数量。举例说明:如果一个OA系统的期望用户为1 000个,只要测试出系统能支持200个并发用户就可以了。 请求响应时间

请求响应时间是指从客户端发出请求到得到响应的整个过程的时间。这个过程从客户端发送一个请求开始计时,到客户端接到从服务器端返回的响应结果计时结束。在某些工具中,请求响应时间通常会被称为“TTLB”,即“Time to last byte”,意思是从发送一个请求开始,到客户端收到最后一个字节的响应为止所耗费的时间。请求响应时间的单位一般为“秒”或“毫秒”。请求响应时间的分解如图1-1所示。

图1-1 Web请求过程分解图

从图1-1可以看出,请求响应时间为“网络响应时间”和“应用程序与系统响应时间”之和,具体由7个部分组成,即(N1+N2+N3+N4)+(A1+A2+A3)。 事务响应时间

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

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

通常用来指单位时间内网络上传输的数据量,也可以指单位时间内处理的客户端请求数量。它是衡量网络性能的重要指标。

但是从用户或业务角度来看,吞吐率也可以用“请求数/秒”或“页面数/秒”、“业务数/小时或天”、“访问人数/天”、“页面访问量/天”来衡量。例如在银行卡审批系统中,可以用“千件/每小时”来衡量系统的业务处理能力。 TPS(Transaction Per Second)

每秒钟系统能够处理的交易或事务的数量。它是衡量系统处理能力的重要指标。TPS是LoadRunner中重要的性能参数指标。 点击率(Hit Per Second)

每秒钟用户向Web服务器提交的HTTP请求数。这个指标是Web应用特有的一个指标:Web应用是“请求-响应”模式,用户发出一次申请,服务器就要处理一次,所以“点击”是Web应用能够处理交易的最小单位。如果把每次点击定义为一次交易,点击率和TPS就是一个概念。不难看出,点击率越大,对服务器的压力也越大。点击率只是一个性能参考指标,重要的是分析点击时产生的影响。

需要注意的是,这里的点击不是指鼠标的一次“单击”操作,因为在一次“单击”操作中,客户端可能向服务器发出多个HTTP请求。 资源利用率

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

资源利用率主要针对Web服务器、操作系统、数据库服务器、网络等,是测试和分析瓶颈的主要参数。在性能测试中,要根据需要采集具体的资源利用率参数来进行分析。

1.2 全面性能测试模型

1.2 全面性能测试模型

通过前面的内容可以看出,性能测试的很多内容都是关联的。这就提供了一条思路:性能测试的很多内容可以经过一定的组织来统一进行。统一开展性能测试的最大好处是,可以按照由浅入深的层次对系统进行测试,进而减少不必要的工作量,以实现节约测试成本的目的。为此,本书提出了“全面性能测试模型”。

“全面性能测试模型”提出的主要依据是:一种类型的性能测试可以在某些条件下转化成为另一种类型的性能测试,而这些测试的实施方式很类似。例如:对一个网站进行测试,模拟10个到50个用户就是常规的性能测试。当用户增加到1000乃至上万时就变成了压力/负载测试。如果同时对系统进行大量的数据查询操作,就包含了大数据量测试。

在“全面性能测试模型”中,把常见的性能测试分为8个类别,然后结合测试工具把性能测试用例归纳为5类来进行设计。下面首先介绍这8个性能测试类别的主要内容: 预期指标的性能测试

系统在需求分析和设计阶段都会提出一些性能指标,完成和这些指标相关的测试是性能测试的首要工作。本模型把针对预先确定的一些性能指标而进行的测试称为预期指标的性能测试。

这些指标主要指诸如“系统可以支持1000个并发用户”、“系统响应时间不得长于10秒”等这些在产品说明书等文档中规定得十分明确的内容。对这种预先承诺的性能要求,测试小组应该首先进行测试验证。 独立业务性能测试

独立业务实际是指一些与核心业务模块对应的业务,这些模块通常具有功能比较复杂、使用比较频繁、属于核心业务等特点。这类特殊的、功能比较独立的业务模块始终都是性能测试的重点。因此,不但要测试这类模块和性能相关的一些算法,还要测试这类模块对并发用户的响应情况。

核心业务模块在需求设计阶段就可以确定,在集成或系统测试阶段开始单独测试其性能。如果是系统类软件或特殊应用领域的软件,通常从单元测试阶段就开始进行测试,并在后继的集成测试、系统测试、验收测试中进一步进行,以保证核心业务模块的性能稳定。何时开始测试核心模块主要由性能测试策略决定,读者可以参考1.2.2节“性能测试用例模型”部分。

组合业务性能测试

通常所有的用户不会只使用一个或几个核心业务模块,一个应用系统的每个功能模块都可能被使用到。所以性能测试既要模拟多用户的“相同”操作(这里的“相同”指很多用户使用同一功能),又要模拟多用户的“不同”操作(这里的“不同”指很多用户同时对一个或多个模块的不同功能进行操作),对多项业务进行组合性能测试。组合业务测试是最接近用户实际使用情况的测试,也是性能测试的核心内容。通常按照用户的实际使用人数比例来模拟各个模板的组合并发情况。

由于组合业务测试是最能反映用户使用情况的测试,因而组合测试往往和服务器(操作系统、Web服务器、数据库服务器)性能测试结合起来进行。在通过工具模拟用户操作的同时,还通过测试工具的监控功能采集服务器的计数器信息,进而全面分析系统的瓶颈,为改进系统提供有利的依据。 疲劳强度性能测试

疲劳强度测试是指在系统稳定运行的情况下,以一定的负载压力来长时间运行系统的测试。其主要目的是确定系统长时间处理较大业务量时的性能。通过疲劳强度测试基本可以判断系统运行一段时间后是否稳定。

大数据量性能测试

大数据量测试通常是针对某些系统存储、传输、统计查询等业务进行大数据量的测试。主要测试运行时数据量较大或历史数据量较大时的性能情况,这类测试一般都是针对某些特殊的核心业务或一些日常比较常用的组合业务的测试。

由于大数据量测试一般在投产环境下进行,所以把它独立出来并和疲劳强度测试放在一起,在整个性能测试的后期进行。大数据量测试可以理解为特定条件下的核心业务或组合业务测试。 网络性能测试

网络性能测试主要是为了准确展示带宽、延迟、负载和端口的变化是如何影响用户响应时间的。在实际的软件项目中,主要是测试应用系统的用户数目与网络带宽的关系。网络性能测试一般有专门的工具,本书不加详述。网络测试的任务通常由系统集成人员来完成。 服务器性能测试(操作系统、Web服务器、数据库服务器)

服务器性能测试主要是对数据库、Web服务器、操作系统的测试,目的是通过性能测试找出各种服务器的瓶颈,为系统扩展、优化提供相关的依据。 一些特殊测试

主要是指配置测试、内存泄漏测试等一些特殊的Web性能测试。这类性能测试或/和前面的测试结合起来进行,或者在一些特殊的情况下独立进行,本书重点讨论前一种情况。后一种情况由于投入较大往往通过特有的工具进行,可以不纳入性能测试的范畴。

“全面性能测试模型”是在以上性能测试分类和总结的基础上提出来的,主要包含3部分内容:

第1部分:性能测试策略模型,这是整个性能测试模型的基础。软件类型决定着性能测试的策略,同时用户对待软件性能的态度也影响性能测试策略的制定。本部分内容主要结合软件类型和用户特点来讨论性能测试策略制定的基本原则和方法。

第2部分:性能测试用例模型,这是整个性能测试模型的核心部分。其主要思想就是结合测试工具,把以上性能测试的8项内容进一步归纳,形成5类测试用例: l 预期指标的性能测试; l 并发用户的性能测试;

l 疲劳强度和大数据量的性能测试; l 服务器性能测试; l 网络性能测试。

在具体的测试设计中,性能测试用例往往和测试工具结合起来,把服务器、网络性能测试的用例设计与前三种类型结合起来。例如LoadRunner就可以在进行压力测试的同时,完成后面两类测试的数据采集工作。因此,后面两部分的测试用例只进行总体设计就可以了。

第3部分:模型的使用方法。本部分内容讨论如何在工作中使用“全面性能测试模型”。 1.2.1 性能测试策略模型

本节主要介绍性能测试策略的制定方法。性能测试策略一般从需求设计阶段就开始讨论如何制定了,它决定着性能测试工作将要投入多少资源、什么时间开始实施等后继工作的安

排。其制定的主要依据是“软件自身特点”和“用户对性能的关注程度”两个因素,其中软件的自身特点起决定作用。

软件按照用途的不同可以分为两大类:系统类软件和应用类软件。系统类软件通常对性能要求比较高,因此性能测试应该尽早介入。应用类软件分为特殊类应用和一般类应用,特殊类应用主要指银行、电信、电力、保险、医疗、安全等领域类的软件,这类软件使用比较频繁,用户较多,一般也要较早进行性能测试;一般类应用主要指一些普通应用,例如办公自动化软件、MIS系统等。一般应用类软件多根据实际情况来制定性能测试策略,例如OA系统,既可以早开始,也可以最后进行性能测试,这类软件受用户因素影响比较大。

按对性能重视程度的不同一般可以将用户分为4类,即高度重视、中等重视、一般重视、不重视。这么划分主要是为了说明用户对性能测试的影响。实际上,用户不关注性能并不意味着测试人员就可以忽略性能测试,但是如果用户特别关注系统性能,那么测试人员也要特别重视性能测试工作。表1-1列出了性能测试策略制定的基本原则。

注意:这里的用户是广义范围的用户,包括所有和产品有利害关系的群体。因而不单单指最终使用产品的用户,这些用户既可以是提出需求的产品经理,也可以是公司的董事会成员,甚至是项目的研发人员。 表1-1 性能测试策略制定的基本原则 软件类别 用户重视程度 高度重视 中等重视/一般重视 不重视 应用类软件 一般类应用 特殊类应用 设计阶段开从设计阶段从设计阶段就始进行一些规就开始针对系开始针对系统划工作,主要统架构、数据架构、数据库设在系统测试阶库设计等方面计等方面进行段开始进行性进行规划,从规划,从根源来能测试实施 根源来提高性提高性能; 可以在系统能; 系统类软件一特殊应用类测试阶段的功般从单元测试能测试结束后软件一般从单阶段开始进行进行性能测试 元测试阶段开性能测试实施始进行性能测工作,主要是测可以在软件试实施工作,试一些和性能发布前进行性主要是测试一相关的算法或能测试,提交些和性能相关模块 测试报告即可 的算法或模块 系统类软件 从表1-1中可以看出:(1)“系统类软件”、“特殊应用类软件”应该从设计阶段开始进行性能测试;(2)制定性能测试策略的主要依据是软件的特点,用户对待系统性能的态度影响性能测试策略,但不起决定作用。

软件的特点决定性能测试策略的另外一个重要原因是“一般应用类软件”本身对性能要求不高,发生性能问题的概率较小。因此可以通过提高硬件配置来改善运行环境,进而提高性能。不过这也不是普遍适用的原则。例如一个几千用户使用的OA系统,仍然要高度重视性能,不管客户对待系统性能是什么态度。

虽然从硬件方面解决性能问题往往更容易做到,同时还可以降低开发成本,但是也不能要求用户进行过大的硬件投入,否则会降低“客户满意度”。调整性能最好的办法还是软硬件相结合。

“用户对待系统性能的态度影响性能测试策略,但不起决定作用”的根本原因是,产品最终是要交付给用户使用的,而不是做出来给用户欣赏的。因此,不管用户是否重视性能测试,甚至根本不关心,对于性能要求较高的软件产品也应按照表1-1的策略来执行性能测试。只是如果用户特别重视产品性能,意味着测试团队可能要进行更多的成本投入。

下面是一些性能测试策略制定的案例。

案例一

一个银行卡审批业务系统的性能测试策略制定。这个项目的性能测试策略从立项时开始制定,贯穿整个项目的执行过程。

银行卡业务系统属于特殊应用软件,加上用户高度重视性能,因而采取的策略是从设计阶段就开始进行性能测试的准备工作。案例的详细内容如表1-2所示。

表1-2 某银行项目测试策略制定案例 产品类型 项目背景 用户要求 银行卡审批业务系统,使用非常频繁,业务量每年达到200万次左右,属于银行领域的特殊应用软件 系统属于二次开发。前一开发商在系统开发完成后没有通过性能测试,当100个用户并发访问系统时就会造成数据库服务器崩溃。因此从项目启动开始,性能测试就已经成为用户关注的焦点 用户提出性能首先要过关,否则功能再好也不会投产 从系统设计阶段开始进行性能测试准备工作。测试人员主要参加系统的设计、评审。前一开发商失利的重要原因是数据库设计不合理,所以重点讨论了数据库的设计 系统设计阶段,完成了性能测试方案的设计 单元测试阶段,通过测试工具对一些重要模块的算法进行了测试。主要是一些并发控制算法的性能测试,测试对象是一些核心业务模块 集成测试阶段进行组合模块的性能测试 整个系统测试阶段都在进行性能测试,性能测试和功能测试同步进行。对功能测试引起的一些相关修改,立刻进行性能测试 验收测试阶段,在用户现场的投产环境进行性能测试,根据测试结果对系统运行环境进行调优,以达到更好的运行效果 性能测试策略

案例二

一个OA系统的测试案例,其性能测试策略和案例一差别很大,具体内容如表1-3所示。 表1-3 某OA项目测试策略制定案例 产品类型 项目背景 用户要求 性能测试策略

企业办公系统,用户数目在1000人以内,主要是一些信息的发布,以及公文流转、收发邮件等功能。软件系统的地位属于辅助办公功能。因此该类软件属于一般类型的应用软件,对性能要求不高,性能测试不属于重要工作 已有稳定产品在工作。主要是按照客户的个性化需求进行二次开发 客户提出了性能方面的需求:要求系统响应时间要加快,可以满足2000个用户使用的需要 系统测试阶段开始进行性能测试准备工作,完成测试用例设计。其目标主要是评估系统性能,根据测试结果对系统进行一定的优化 验收测试阶段在用户现场执行性能测试用例,根据测试结果进行一定的调优工作,提交测试报告给用户以便进行系统验收 案例三 一个门户系统的测试案例,具体内容如表1-4所示。 表1-4 某门户项目测试策略制定案例 产品类型 主要用于一些单位信息的发布,用户在50人以下。因此该类软件属于一般类型的应用软件,对性能要求很低 软件运行的硬件环境较好 用户没有提出具体的要求 验收测试在使用现场进行,根据测试结果进行一定的调优工作,提交测试报告给用户,以便进行系统验收 项目背景 用户要求 性能测试策略 仅仅三个案例不足以说明所有性能测试策略制定的方法,但是通过这三个案例可以对性能测试策略的制定有更进一步的了解,能够认识到性能测试策略的制定由软件自身特点决定,同时受用户态度的影响。实际上,软件项目的背景、软件运行环境等许多方面都会影响性能测试策略的制定。因此,本节提出的只是基本的参考方案。制定测试策略是十分复杂的工作,最有效的方法就是“从实际出发”。项目的特点千差万别,只有把用户当成“上帝”,充分为用户考虑,才可以制定出合理的性能测试策略。

本节介绍了性能测试策略制定的基本思路和方法。性能测试策略是后期性能测试工作的基础,决定着性能测试工作的投入。因此,要充分意识到这一工作的重要性,认识到只有做好了前期的“路线”制定工作,才可以走对后面的“道路”。 1.2.2 性能测试用例模型

“性能测试用例模型”是“全面性能测试模型”的核心内容。限于篇幅和本书主旨,本节仅对“性能测试用例模型”做概要介绍。关于“性能测试用例模型”以及“全面性能测试模型”更详细的内容,读者可以参考作者的另一本专著《Web性能测试实战》。

在前面的内容中,已经介绍了性能测试分为8个方面。而在“性能测试用例模型”中,则融合了性能、强度、压力、负载等多方面测试内容,对性能测试进行了重新组织和分类,最终归纳出五类性能测试用例。下面介绍各类性能测试用例包含的内容以及设计方法。 预期性能指标测试用例

所谓预期或预定性能指标,就是指一些十分明确的、在系统需求设计阶段预先提出的、期望系统达到的,或者向用户保证的性能指标,这些指标是性能测试的首要任务。针对每个指标都要编写一个或多个测试用例来验证系统是否达到要求,如果达不到目标,则需根据测试结果来改进系统的性能。

预期指标的用例设计比较简单,主要参考需求和设计文档,把里面十分明确的性能要求提取出来即可。指标中通常以单用户为主,如果涉及并发用户内容,则归并到并发用户测试用例中进行设计,遇到其他内容亦可采用同样的方法处理。 用户并发性能测试用例

本节的用户并发测试融合了前面提到的“独立业务性能测试”和“组合业务性能测试”两类内容,主要是为了使性能测试按照一定的层次来开展。独立业务性能测试实际上就是核心业务模块的某一业务的并发性能测试,可以理解为“单元性能测试”;组合业务的性能测试是一个或多个模块的多项业务同时进行并发性能测试,可以理解为“集成性能测试”。“单元性能测试”和“集成性能测试”两者紧密相连,由于这两部分内容都是以并发用户测试为主,因此把这两类测试合并起来通称为“用户并发性能测试”。

用户并发性能测试要求选择具有代表性的、关键的业务来设计测试用例,以便更有效地评测系统性能。当编写具体的测试用例设计文档时,一般不会像功能测试那样进行明确的分类,其基本的编写思想是按照系统的体系结构进行编写的。很多时候,“独立业务”和“组合业务”是混合在一起进行设计的。

单一模块本身就存在“独立业务”和“组合业务”,所以性能测试用例的设计应该面向“模块”,而不是具体的业务。在性能测试用例设计模型中,用户并发测试实际就是关于“独立核心模块并发”和“组合模块并发”的性能测试。

用户并发性能测试的详细分类如图1-2所示。

图1-2 用户并发性能测试的分类示意图

独立核心模块(以下简称“核心模块”)并发性能测试的重点是测试一些系统重要模块独立运行的情况,因此可以将其理解为“单元性能测试”。只有这些决定系统性能的“核心单元”性能稳定,后面的性能测试才有意义。核心模块并发性能测试是整个性能测试工作的基础。

组合模块并发性能测试是最能反映用户实际使用情况的测试,是在前面各个核心模块运行良好的基础上、把系统的一些具有耦合关系的模块组合起来的测试,因此可以理解成 “集

成性能测试”。组合模块用户并发性能测试最重要的是模拟实际用户比较常见的场景,只有这样才可以真实地反映用户使用系统的情况,进而发现系统的瓶颈和其他一些性能问题。 疲劳强度与大数据量测试

疲劳强度测试属于用户并发测试的延续,因此测试内容仍然是“核心模块用户并发”与“组合模块用户并发”。在实际工作中,一般通过工具模拟用户的一些核心或典型的业务,然后长时间地运行系统,以检测系统是否稳定。

大数据量测试主要是针对那些对数据库有特殊要求的系统而进行的测试,例如电信业务系统的手机短信业务。由于有的用户关机或不在服务区,每秒钟需要有大量的短信息保存,同时在用户联机后还要及时发送,因此对数据库性能有极高的要求,需要进行专门测试。编写本类用例前,应对需求设计文档进行仔细分析,提出测试点。

大数据量测试分为3种:

l 实时大数据量测试:模拟用户工作时的实时大数据量,主要目的是测试用户较多或某些业务产生较大数据量时,系统能否稳定地运行;

l 极限状态下的测试:主要是测试系统使用一段时间后,即系统累积一定量的数据后,能否正常地运行业务;

l 前面两种的结合:测试系统已经累积较大数据量时,一些运行时产生较大数据量的模块能否稳定地工作。 网络性能测试

网络性能测试的用例设计主要有以下两类:

l 基于硬件的测试:主要通过各种专用软件工具、仪器等来测试整个系统的网络运行环境,一般由专门的系统集成人员来负责,不在本书的研究范围之内;

l 基于应用系统的测试:在实际的软件项目中,主要测试用户数目与网络带宽的关系。通过测试工具准确展示带宽、延迟、负载和端口的变化是如何影响用户响应时间的。例如,可以分别测试不同带宽条件下系统的响应时间。 服务器性能测试

服务器性能测试主要有两种类型:

l 高级服务器性能测试:主要指在特定的硬件条件下,由数据库、Web服务器、操作系统相应领域的专家进行的性能测试。例如,数据库服务器由专门的DBA来进行测试和调优。这类测试一般不由测试工程师来完成,所以不在本书的研究范围之内;

l 初级服务器性能测试:主要指在业务系统工作或进行前面其他种类性能测试的时候,监控服务器的一些计数器信息。通过这些计数器对服务器进行综合性能分析,找出系统瓶颈,为调优或提高性能提供依据。 1.2.3 模型的使用方法

“全面性能测试模型”是针对性能测试而提出的一种方法,主要是为了比较全面地开展性能测试,使性能测试更容易组织和开展。本模型包含了测试策略制定的通用方法和测试用例设计的通用方案。其中测试用例的设计覆盖了应用软件、服务器、操作系统等多方面内容,按照由浅入深的层次对性能测试进行合理的组织。

“全面性能测试模型”是一种从很多性能测试项目抽象出来的方法论,主要用来指导测试,一般不适合具体的性能测试项目,因为任何一个项目都会有它的特定背景。要想通过“全面性能测试模型”做好性能测试工作,首先要制定好性能测试策略,同时还要按照一些基本指导原则来使用“性能测试用例模型”的内容。这些原则主要包括如下内容:

l 测试策略遵从最低成本原则。全面性能测试本身是一种高投入的测试,而很多公司在测试上的投入都比较低;性能测试同时又是全部测试工作的一部分,很多项目只能进行一些重要的性能测试内容。这就决定了测试负责人制定性能测试策略时在资源投入方面一定要遵从最低成本化原则。最低成本的衡量标准主要指“投入的测试成本能否使系统满足预先确定的性能目标”。只要经过反复的“测试—系统调优—测试”后,系统符合性能需求并有一定的扩展空间,就可以认为性能测试工作是成功的。反之,如果系统经过测试后不能满足性能需求或满足性能需求后仍须继续投入资源进行测试,则可以认为是不合理的。

l 策略为中心原则。本原则不但对性能测试工作有效,对其他类型的测试工作同样具有指导意义。测试策略不但决定了测试用例设计的主要内容,还决定着实施测试工作时如何根据项目的实际情况进行处理。例如当项目时间比较紧张时,就可以按照测试用例的优先级只执行一部分性能测试用例。因此,性能测试策略应该贯穿整个性能测试的全过程。

l 适当裁剪原则。裁剪原则主要是针对性能用例设计而言的。性能测试用例设计模型主要是针对电信、银行等特殊领域的应用而提出的,包含的测试内容比较全面,而这类项目的性能

测试一般周期较长、投入较大。一些银行项目的性能测试周期可能会超过一年。要想性能测试用例设计模型在大多数测试项目中适用,就必须对测试用例模型包含的内容进行合理的裁剪。这样做主要是为了适合特定项目的测试需求,进而节约测试成本。

裁减的主要依据是性能测试策略。根据策略制定方法制定出测试策略,然后从“5类性能测试用例”中选择适当的类别来编写测试用例。例如有些要求不高的静态门户网站,用户没有提出性能方面的要求,可以只测试用户并发情况作为系统性能的参考。

l 完善模型原则。本模型只是作者工作经验的总结,由于性能测试任务都有自己的项目背景,因而需要对模型内容进行不断的调整、补充、完善,使之适合更多的性能测试工作。具体来说,不断完善就是要在工作中不断总结经验,形成自己的“全面性能测试模型”。只有“自己的”测试模型,才是最符合需要的模型。

l 模型具体化原则。模型具体化是指把模型运用到具体的项目中去,这是前面所有指导原则的终极目标。如果只记住模型的条条框框,生搬硬套框架来设计测试,只能得到适得其反的结果。要想使模型在性能测试工作中发挥作用,只有根据实际项目的特点制定合理的性能测试策略、编写适当的性能测试用例,并在测试实施中灵活地执行测试方案才是上策。

综合上面的分析可以看出,模型的使用可以概括为两个字——活用。要想真正做好性能测试工作,最有效的办法就是在掌握基本理论和方法后,在工作中不断地探索和总结,形成自己的“全面性能测试模型”。

1.3 性能测试调整基础

所谓性能测试调整是为了改善系统某些方面的性能而对系统软件或硬件进行的修改。性能调整不是测试人员的职责,性能测试工程师的主要任务是发现并定位性能问题。对于性能测试中发现的问题,通常由性能测试工程师、DBA、系统管理员、开发人员共同来解决。但是对于测试人员,了解调整的相关知识则是十分必要的。

在性能测试工作中经常会提到“性能调优”或“系统调优”等概念。实际上,“性能调优”或“系统调优”只是性能调整的一部分内容。例如,可能为了让某些部分“更优”而把某些部分调得“不优”,因此本书使用“性能调整”这一说法。

本节主要讨论性能调整的基础知识。性能调整应该按照一定的顺序进行,主要包括下面五个步骤:

确定问题

首先根据测试结果确定系统是否存在问题,重点是发现系统的瓶颈。如果存在,就应该确定是什么问题,并对问题进行正确的定位。确定系统问题可从下面几个方面入手: l 检查应用程序代码:通常情况下,很多程序的性能问题都是“写”出来的。因此对于发现瓶颈的模块,应该首先检查代码;

l 调整数据库配置:数据库配置经常会引起整个系统运行缓慢,一些诸如Oracle的大型数据库都需要DBA进行正确的参数调整才能投产;

l 调整操作系统配置:操作系统配置不合理也可能引起系统瓶颈;

l 检查硬件设置:磁盘速度、内存大小等都是引起瓶颈的原因,因此这些也是分析的重点; l 检查网络:网络负载过重会导致网络冲突和网络延迟。

同时,还要对系统的使用情况进行调查,例如: l 是否听到了很多用户的抱怨?

l 某些操作的响应时间是否随着使用时间的增长而增长? l CPU的使用率是否很低而I/O的使用率却很高? l 使用过程中性能是否稳定?

系统性能问题不是显而易见的,要仔细查找才能正确地定位。 确定原因

确定系统存在问题后就要仔细进行分析,进而确定引起问题的原因。确定原因很大程度上靠的是团队的经验和技术能力,涉及的知识有操作系统、数据库、网络、程序开发等许多方面。

和确定性能问题一样,确定原因仍然要广泛地搜集信息。通常要进行以下的分析: l 问题的影响是什么:响应速度还是吞吐量,或者其他问题? l 是大多数用户还是少数用户遇到了问题?如果是少数用户,这几个用户与其他用户的操作有什么不同?

l 系统资源监控的结果是否正常,如CPU的使用是否到了极限?I/O情况如何? l 问题是否集中在某一类模块中? l 是客户端还是服务器出现问题? l 系统硬件配置是否合理?

l 实际负载是否超过了系统的负载能力? l 是否未对系统进行优化?

通过这些分析以及系统的一些具体表现,可以对系统瓶颈有更深入的了解,进而分析出真正的原因。

确定调整目标和解决方案

在分析出问题发生的原因后,测试人员和系统调整人员首先要确定调整目标,然后设计解决方案。确定调整目标的主要作用是明确何时停止系统调整,否则工作将永无尽头。

每个系统都有不同的特点,因此调整目标可能各有不同。例如,下面这些都是系统的调整目标:

l 提高系统吞吐量; l 缩短响应时间; l 更好地支持并发;

设计解决方案的主要依据就是这些调整目标。有了明确的方案和目标,就可以进行后面的工作了。 测试解决方案

实施解决方案后,就要对方案进行测试。可以使用以前的测试用例来进行测试,验证系统是否解决了性能问题。测试解决方案尽量要在仿真环境下进行,因为在生产环境下可能会带来破坏,除非充分估计了测试的风险,并且准备了万全的补救方案。 分析调整结果

性能调整的最后一步是分析调整结果,如果问题没有得到解决,则要重复前面的工作。在测试系统调整方案过程中,要经常分析所做的工作。如果没能准确定位问题或调整方案不正确,可能会达不到预期目标。要尽早发现这些错误,以使工作早些回到正确的轨道上来。

分析结果时主要考虑下面的问题: l 系统调整是否达到或超出了预定目标?

l 系统是整体性能得到了改善,还是牺牲了某部分性能来解决问题的? l 调整是否可以结束了?

达到预期目标后,调整工作基本就可以结束了。 1.4 如何做好性能测试

多数企业都想使产品获得高性能,以降低投产后的风险。但是现实中的性能测试工作却经常不受重视,常会碰到“走过场”或“拖到整个项目最后进行”的情况,甚至有时会做很多无意义的性能测试。此外,多数企业的测试人员能力水平不高,这也是导致性能测试不过关的原因。

根据作者多年的经验,要想做对性能测试应该从管理与技术两个方面入手。 按照规范的管理流程开展测试工作

软件性能的低下很多时候是由于系统架构设计不好或代码效率低下而引起的,如果上线后发现性能问题往往已很难补救。因此性能测试应该按照规范的流程来执行,尽量把问题消灭在产品上线以前。

根据多数企业的实际情况,性能测试应该分为开发与用户现场两个阶段来进行。 严格地讲,性能测试应该按照测试环境的软、硬件配置高低分为两个阶段。只是由于开发阶段的软、硬件配置相对较低,而用户现场的投产环境软、硬件配置较高,因此才把性能测试分为开发与用户现场两个阶段。对于拥有先进实验设备甚至实验室的公司,完全可以在开发阶段完成全部的性能测试工作,如果用户现场仍要进行性能测试,则只是简单的验收测试而已。

l 开发阶段的性能测试实施 开发阶段的性能测试主要指软件试运行前的性能测试,即团队内部的性能测试。这一阶段的性能测试是一个反复迭代的过程。

性能测试不是特别重要的项目,这一阶段的性能测试较多关注于软件功能而引起的缺陷。因此主要进行用户并发性能测试,即核心模块并发用户测试与组合模块并发用户测试。此外,可能还会进行一些预期性能指标的性能测试。通过开发阶段的性能测试可以发现一些核心算法问题,最大限度地排除由软件本身引起的问题。

对于系统类软件或特殊应用系统的性能测试,解决其性能问题可能很耗时,所以应该较早地组织硬件资源进行各类性能测试,例如疲劳强度与大数据量测试、服务器性能测试等。

l 用户现场性能测试的实施 用户现场的性能测试有验收测试的“味道”,是开发阶段性能测试工作的延续。这一阶段的性能重点是关注性能测试的整体表现。

可以看出,用户现场的性能测试主要是为了验收与调优。因此对于系统软件和特殊应用系统,性能测试应该尽可能全方位覆盖。而对于一般应用系统,由于风险较低,所以测试范围可以适当缩小以节省成本。用户现场的性能测试主要基于投产环境,测试对象多是即将准备投产的系统,甚至可能是已经投产的系统。投产环境的硬件资源配置通常较高,各类性能测试基本都可以开展。

对于系统软件和特殊领域的应用系统,这一阶段的性能测试主要包含预期指标性能测试、并发用户性能测试、各类服务器性能测试、疲劳强度与大数据量性能测试等内容,基本覆盖了“全面性能测试模型”的各个方面。与开发阶段的性能测试相比,本阶段执行的性能测试用例数量可能会少一些,但是测试用例覆盖的范围与开发阶段的性能测试基本一致。

一般应用系统在用户现场的性能测试通常包含预期指标性能测试与用户并发性能测试,可能也会对服务器进行一定的测试,不过内容通常比较简单。一般应用系统发生性能问题的风险通常不会太高,因此只要通过验收测试即可。 这两个阶段的性能测试都应该按照“需求分析—规划与设计—执行—调优—验证”的顺序来执行。

提高测试人员在性能测试方面的技能

很多时候,由于性能测试人员水平较低,即使进行了测试也不能发现系统潜在的问题,而最终把问题留给了用户。因此,测试执行人员首先要提高自己的素质和技能。

根据作者多年的经验,一个有竞争力的测试人员要具备以下3方面的素质:

l 计算机专业技能 计算机领域的专业技能是测试工程师应该必备的一项素质,这是做好测试工作的前提条件。尽管没有任何IT背景的人也可以从事测试工作,但是一名要想获得更大发展空间或持久竞争力的测试工程师,计算机专业技能是必不可少的。计算机专业技能主要包含3个方面:

(1)测试专业技能。现在,软件测试已经成为一个很有潜力的专业。要想成为一名优秀的测试工程师,首先应该具有扎实的专业基础,这也是本书的编写目的之一。测试工程师应该努力学习测试专业知识,告别简单的“点击”式的测试工作,让测试工作以自己的专业知识为依托。

测试专业知识很多,本书内容主要以测试人员应该掌握的基础专业技能为主。测试专业技能涉及的范围很广:既包括黑盒测试、白盒测试、测试用例设计等基础测试技术,也包括单元测试、功能测试、集成测试、系统测试、性能测试等测试方法,还包括基础的测试流程管理、缺陷管理、自动化测试技术等知识。

(2)软件编程技能。“测试人员是否需要学会编程?”这是测试人员经常提出的问题之一。实际上,由于在我国开发人员待遇普遍高于测试人员,因此能写代码的几乎都去做开发了。很多人是因为做不了开发或者不能从事其他工作才“被迫”从事测试工作。最终的结果则是很多测试人员只能从事相对简单的功能测试,能力相对强一点的则可以借助测试工具进行简单的自动化测试(主要进行脚本录制与修改、回放测试脚本等)。

软件编程技能应该是测试人员的必备技能之一。在微软,很多测试人员都拥有多年的开发经验。因此,测试人员要想得到较好的职业发展,必须能够编写程序。只有能够进行测试开发,才可以胜任诸如单元测试、集成测试、性能测试等难度较大的测试工作。

此外,对于软件测试人员的编程技能的要求也有别于开发人员:测试人员编写的程序应着眼于运行正确,同时兼顾高效率,尤其要体现在与性能测试相关的测试代码编写上。因此测试人员要具备一定的算法设计能力。依据作者的经验,测试工程师至少应该掌握Java、C#、C++之中的一门语言以及相应的开发工具。

(3)网络、操作系统、数据库、中间件等知识。与开发人员相比,测试人员掌握的知识要求更博,“艺多不压身”是个非常形象的比喻。由于测试中经常需要配置、调试各种测试环境,而且在性能测试中还要对各种系统平台进行分析与调优,因此测试人员需要掌握更多网络、操作系统、数据库等方面的知识。

在网络方面,测试人员应该掌握基本的网络协议以及网络工作原理。尤其要掌握一些网络环境的配置知识,这些都是测试工作中经常用到的知识。

操作系统和中间件方面,应该掌握基本的使用及安装、配置等技能。例如,很多应用系统都是基于Unix、linux来运行的,这就要求测试人员掌握其基本的操

作命令以及相关工具软件的使用。而WebLogic、Websphere等中间件的安装与配置方法也需要掌握一些。

数据库知识则是更应该掌握的基础知识。现在的应用系统几乎离不开数据库。因此,不但要掌握基本的安装、配置,还要掌握SQL。测试人员至少应该掌握Mysql、MS Sqlserver、Oracle等常见数据库的使用。

作为一名测试人员,尽管不能精通所有的知识,但要想做好测试工作,应该尽可能地去学习更多的与测试工作相关的专业知识。

l 行业知识 所谓行业主要指测试人员所在企业涉及的领域。例如很多IT企业从事石油、电信、银行、电子政务、电子商务等行业领域的产品开发。行业知识即专业业务知识,是测试人员做好测试工作的又一个前提条件。只有深入了解了产品的业务流程,才可以判断出开发人员实现的功能项是否正确。

很多时候,软件运行起来没有异常,但是功能不一定正确。只有掌握了相关

的行业知识,才可以判断出用户的业务需求是否得到了实现。

行业知识与工作经验有一定关系,只有通过一定的时间积累才能达到较高的水平。 l 个人素养 作为一名优秀的测试工程师,首先要对测试工作有兴趣,因为测试工作在很多时候多少显得有些枯燥。因此,先要热爱测试工作,才能做好测试工作。在个人素养方面,除了具有前面介绍的专业技能和行业知识外,测试人员还应该具有一些基本的品质,即下面的“五心”:

(1)专心:主要指测试人员在执行测试任务的时候不可一心二用。经验表明,高度集中精神不但能够提高效率,还能发现更多的软件缺陷。团队中业绩最棒的往往是做事精力最集中的那些成员。

(2)细心:主要指进行测试工作时要认真执行测试,不可以忽略一些细节。如果不细心,则很难发现某些缺陷,例如一些界面的样式、文字等。

(3)耐心:很多测试工作有时候显得非常枯燥,需要很大的耐心才可以做好。如果做事情浮躁没有耐心,就不会做到“专心”和“细心”,就会让很多软件缺陷从眼前逃过。

(4)责任心:责任心是做好工作必备的素质之一,测试工程师更应该高度负责。如果测试中没有尽到责任,敷衍了事,甚至把测试工作交给用户去完成,这样很可能引起非常严重的后果。

(5)自信心:自信心是目前多数测试工程师都缺少的一项素质,尤其在面对测试开发等工作时,往往认为自己做不到。要想获得更好的职业发展,测试工程师们应该努力学习,建立能“解决一切测试问题”的信心。性能测试人员的要求通常要高于普通测试人员,因此更应该努力去学习相关知识,把测试工作做得更好。

企业良好的管理流程、测试工程师的高技术水平仅仅是做好性能测试的必要条件。实际工作中,性能测试会受其他诸多方面的影响,例如进度与成本压力、测试资源支持度等。但是,只要我们有信心并为之不懈努力,相信一定能够做好性能测试工作! 1.5 本章小结

开展性能测试工作,仅有LoadRunner是远远不够的,深入地理解性能测试理念是做好性能测试工作的前提。因此在开始学习LoadRunner前,应该先搞清楚“性能测试是怎么一回事”,并学会如何设计与组织性能测试。

本章首先从性能测试的基本概念入手来介绍性能测试的相关知识,接着介绍了根据作者多年经验总结的“全面性能测试模型”。借助本模型,可以更加合理地开展性能测试工作。本章还介绍了性能测试调整方面的一些知识,以掌握性能测试调优的“度”。

在本章最后,又对如何做好性能测试进行了一些探讨——性能测试不但需要高超的技能,更需要规范的企业管理流程。

掌握了性能测试的基础知识后,我们将逐步踏入LoadRunner的性能测试世界!

5.1.1 性能分析基础知识

在测试场景执行完成后,很多测试工程师认为最困难的阶段到来了——性能测试结果分析。因此,本章似乎很自然地就成为了最重要的一章,但作者却认为性能测试分析并不是最难的工作。所谓“万丈高楼平地起”,也就说明性能分析的准确性同样取决于此前所做的设计与实施等“地基”是否可靠。因此可以说,性能测试分析仅仅是百米赛跑中的最后二十米而已。当然,这并不是说性能测试分析不重要,因为“最后冲刺的二十米没有跑好”,前面工作做得再好也是徒劳的。由此不难理解,性能测试分析工作开展的根基就是前面测试场景执行的结果。要想保证性能测试分析的结论是正确的,那么测试结果数据首先就应该是正确的,而这也意味着测试场景以及测试执行过程都应该是正确的。

实际上,性能测试从始至终都应该是相当严谨的一项工程,各个阶段的工作环环相扣,因此,性能测试工程师应该认真对待每一个阶段的工作。如果一味地追求找出系统瓶颈,无疑是舍本逐末的做法。

如果脱离实际应用或仅拿出某些孤立的测试结果来介绍LoadRunner的Analysis如何使用,相信很多读者将会一头雾水,仍然不能解决实际问题。因此,本章首先结合案例来讲解

如何分析性能测试结果,然后探讨Analysis的具体使用细节,这也是本章不同于第3、4章的地方。

本章的主要内容如下: n 如何分析性能测试结果 n 如何从分析图中发现问题 n 分析图的处理方法 n Analysis分析报告 5.1 如何分析性能测试结果

在Controller执行的测试场景结束后,首先要做的是判断采集到的结果数据是否真实有效。多数性能测试场景都需要迭代地进行测试,因此很多测试结果本身就不能反映真正的问题,而深入分析这样的结果纯属浪费时间。在本书中,主要探讨如何针对有效的测试结果数据进行分析。

判断测试结果是否有效,通常按下面的步骤进行:

第一步:在整个测试场景的执行过程中,测试环境是否正常。如果在测试过程中出现过异常,那么这样得出的结果往往不准确,无须进行分析。

例如,在测试执行过程中,测试机的CPU利用率经常达到100%、测试环境的网络不稳定、一些系统参数配置不正确等等,这样得出的测试结果没有必要进行分析,应该重新设置测试场景或调整测试环境,再次执行测试。

第二步:测试场景的设置是否正确、合理。测试场景的设置是否正确对测试结果有很大的影响。因此,当测试出现异常时,我们要对场景设置进行分析。

一些新手在使用Controller执行测试时,可能会同时在一台PC上加载全部虚拟用户——例如同时加载1000个虚拟用户,如果客户端来不及处理,就会有很多虚拟用户因不能初始化而失败。失败的根本原因不是被测试的应用服务器不能处理,而是压力根本没有传输过去。正确的做法是增加更多的Generator或逐步加压,使测试场景运行起来。

第三步:测试结果是否直接暴露出系统的一些问题。对测试场景的整个执行过程,没有必要对压力下系统运行正常的结果进行分析,因为这样的结果不能反映出系统的性能问题,应该进一步调整场景(比如增大压力)进行测试。在测试过程中使系统表现不正常的测试场

景生成的结果则要进行深入分析。实际上,分析能够反映性能问题的测试结果才是性能分析阶段的主要工作。

测试结果直接暴露系统存在性能问题的情形很多,例如在测试过程中一些用户事务响应时间过长、系统支持的最大并发用户数过低、系统的应用服务器CPU利用率过高或内存不足等。对这类测试结果,性能测试人员需要借助Analysis对其进行深入分析,以发现一些潜在的性能问题。

本节先介绍性能测试分析的基础知识,然后介绍LoadRunner Analysis的使用基础,最后结合案例介绍如何找出并解决系统的性能问题。 5.1.1 性能分析基础知识 性能分析的基本原则

确定测试结果有效之后,接下来就要开始对测试数据进行深入的挖掘了。面对测试工具产生的纷繁复杂的原始测试数据,如何来进行分析呢?一个普遍遵循的原则是“由外而内,由表及里,层层深入”,如图5-1所示。

图5-1 性能分析原则示意图

对于一个应用系统,性能开始出现下降的最直接表象就是系统的响应时间变长。于是,系统响应时间成为分析性能的起点。性能分析的原则如图5-1所示,首先应该从原始测试数据中查看系统响应时间,判断它是否满足用户性能的期望。如果不能满足,则说明系统的性能出现了问题。发现系统存在问题后,就要判断系统在哪个环节出现了瓶颈。

现在的IT系统架构极其复杂,任何一个环节出现瓶颈,都会导致系统出现性能问题。要准确地判断瓶颈在什么地方,的确是一个棘手的问题。不过,任何复杂的系统都分为网络和服务器两部分。因此要考察的第二个问题就是:系统的瓶颈是出现在网络环节,还是服务器环节?

如图5-2所示,用户从客户端发起的请求数据包经过网络,传递到应用服务器,最后到达数据库服务器,服务器处理完毕后按原路返回到客户端。在这个处理过程中,可以把整个

时间分为两段:一段是Tn,即网络的响应时间;一段是Ts,即服务器的响应时间,包括应用服务器和数据库服务器的响应时间。对比Tn和Ts,就很容易知道系统在哪些环节的响应时间比例较大。

图5-2 客户交易分解图

只要判断出系统的瓶颈是出现在网络或是服务器段,就可以层层推进对相应环节的组件响应时间进行深入分析,直到最后找到造成性能问题的根本原因。

借助LoadRunner的分析组件Analysis,很容易按照“由外而内,由表及里,层层深入”的原则进行分析,快速将问题定位。例如从图5-3中可以直接看出瓶颈出现在网络上。

图5-3 客户请求第一个Buffer的分解示例 性能分析任重而道远

看了前面的内容,也许很多人会以为性能分析非常容易,借助工具即可完成,但实则不然。即使有了正确的测试结果,也不一定能对系统的性能问题进行正确定位。例如,服务器的内存不够可能会引起较大的磁盘I/O,进而导致CPU利用率居高不下,其根本原因可能是

程序内部存在内存泄漏,而不是内存瓶颈。这类问题不但要靠经验,更要靠对系统的深入了解。

不难看出,性能测试是难度较大的一项工作,绝不是一蹴而就的事情。根据作者的经验,最好的办法是把性能分析贯穿于性能测试过程的始末,所有人员都应该给予高度关注。

实际上,性能测试分析从测试场景执行时就开始了,而不是仅仅在测试结束后才进行的。例如,在测试执行过程中可以借助分析数据库,观察事务实时响应时间来发现一些问题。

除了这些通用的方法外,性能测试分析人员还应该在测试设计、执行、分析等各阶段把工作做透,只有这样才能把性能测试工作做好。 5.1.2 Analysis使用基础

在测试场景执行过程中,LoadRunner采集了虚拟用户、操作系统、应用服务器等各种运行数据,这些数据成为分析系统性能的重要参考资料。当测试场景运行结束后,就可以通过Analysis对这些测试结果进行专门的分析,以发现系统的潜在问题。

LoadRunner的Analysis是一个独立模块,本节将介绍它的主要功能以及基本使用方法。在后面的5.2节中,将详细介绍如何借助各类数据图表来分析系统的性能问题。 Analysis的基本功能及使用

启动Analysis有4种方式:在Controller启动场景前选中其菜单的“Run→Auto Load Analysis”;在Controller工具栏中点击第一个

图标;在Controller工具栏中点击第二个

图标;从开始菜单依次点击“Mercury LoadRunner→Applications→Analysis”。其中,前两种方式在打开Analysis后会自动分析当前场景的运行结果,后两种方式仅打开Analysis应用程序,需要手动选择测试结果文件来产生分析图。

在测试结束并完成测试结果数据收集后,就可以启动Analysis打开测试结果文件,将其导入Microsoft Access数据库,然后按照设置的模板打开默认的结果分析图。通常的分析器默认界面如图5-4所示。

利用Analysis进行分析的第一步是查看分析概要报告(Analysis Summary),图5-4中显示的即为分析概要报告。分析概要报告展示了场景运行的统计信息、事务响应时间概述、HTTP响应概述(对于Web测试)等。

在分析概要结果中,重点查看虚拟用户的运行情况和事务综述。对虚拟用户,主要查看最大并发用户数目;对事务综述,则要查看最大、最小、平均、“90%”事务最大响应时间、通过事务数量、失败事务数量等。

图5-4 Analysis的默认分析概要界面

在图5-4所示的Analysis界面中,点击

界面,在这里可以查看Analysis提供的全部分析图。

从图5-5中可以看出,对于一个和Web相关的测试结果,Analysis主要提供了六大类分析图。下面简要介绍各类分析图的含义及用途。

将进入到图5-5所示的新的分析图

图5-5 打开新的分析图界面

虚拟用户(Vusers)图 虚拟用户图分为运行状态的虚拟用户图、虚拟用户概要图和集合点图3类。主要借助其查看场景与会话的虚拟用户行为。

Errors图 Errors图主要有错误统计、每秒错误数量两类。借助Errors图可以发现服务器什么时间发生错误以及错误的统计信息,可以分析服务器的处理能力。

事务(Transactions)图 Analysis和事务相关的分析图表有事务综述图、事务平均响应时间图、每秒通过事务数图、每秒通过事务总数图、事务性能摘要图、事务响应时间与负载分析图、事务响应时间(百分比)图、事务响应时间分布图等,通过这些图表可以很容易分析应用系统事务的执行情况。

Web资源(Web Resources)图 Web资源图主要有Web服务器的吞吐率图、点击率图、返回的HTTP状态代码图、每秒HTTP响应数图、每秒重试次数图、重试概述图、服务器连接数概要图、服务器每秒建立的连接数量图等。借助Web资源图,可以深入地分析服务器的性能。

网页细分(Web Page Breakdown)图 在Controller中启动网页细分功能后,才可以在Analysis中查看网页细分图,启动细分功能的具体步骤是:在Controller菜单中选择“Diagnostics→Distribution”进入图5-6所示的界面,在图5-6中同时选中“Enable the following diagnostics”和“Web Page Diagnostics(Max Allowed Distribution 10%)”复选框。

图5-6 启动网页细分图功能

网页细分图主要有页面分解总图、页面组件细分图、页面组件分解(随时间变化)图、页面下载时间细分图、页面下载时间细分(随时间变化)图、第一次缓冲时间细分图、第一次缓冲时间细分(随时间变化)图、已下载组件大小图。借助网页细分图可以分析页面元素是否影响事务响应时间。

系统资源(System Resources)图 系统资源图显示在场景运行期间,由联机监控获得的系统资源使用情况。要想获得系统资源图,必须预先指定相关的计数器。关于监控系统资源的方法,可以参考第3章的相关内容。

在5.4节中,我们将详细介绍如何查看各类具体的分析图。 如何看Analysis分析图

面对Analysis提供的几十个测试结果分析图,很多人会感到无所适从,不知如何入手。实际上,性能测试分析要求执行人员更加谨慎和细心,不能放过任何一个缺陷,尤其要深入到系统内部来进行分析。同时,当分析结果时还应该借助Analysis以外的各种分析工具。例如,可以借助Oracle提供的监控与分析工具,也可以借助WebLogic提供的监控与分析工

具,要想尽一切办法来发现系统瓶颈。在 5.1.3节的案例中,Oracle自带的SQL分析功能对性能定位起了至关重要的作用。

下面介绍一些通用的性能测试分析流程。 第一步:从分析Summary的事务执行情况入手。

Summary主要是判定事务的响应时间与执行情况是否合理。如果发现问题,则需要做进一步分析。通常情况下,如果事务执行情况失败或响应时间过长等,都需要做深入分析。

下面是查看分析概要时的一些原则:

用户是否全部运行,最大运行并发用户数(Maximum Running Vusers)是否与场景设计的最大运行并发用户数一致。如果没有,则需要打开与虚拟用户相关的分析图,进一步分析虚拟用户不能正常运行的详细原因;

事务的平均响应时间、90%事务最大响应时间用户是否可以接受。如果事务响应时间过长,则要打开与事务相关的各类分析图,深入地分析事务的执行情况;

查看事务是否全部通过。如果有事务失败,则需要深入分析原因。很多时候,事务不能正常执行意味着系统出现了瓶颈;

如果一切正常,则本次测试没有必要进行深入分析,可以进行加大压力测试; 如果事务失败过多,则应该降低压力继续进行测试,使结果分析更容易进行; ……

上面这些原则都是分析Summary的一些常见方法,读者应该灵活使用并不断地进行总结与完善,尤其要注意结合实际情况,不能墨守成规。

第二步:查看负载发生器和服务器的系统资源情况。

查看分析概要后,接下来要查看负载发生器和待测服务器的系统资源使用情况:查看CPU的利用率和内存使用情况,尤其要注意查看是否存在内存泄漏问题。这样做是由于很多时候系统出现瓶颈的直接表现是CPU利用率过高或内存不足。

应该保证负载发生器在整个测试过程中其CPU、内存、带宽没有出现瓶颈,否则测试结果无效。而待测试服务器,则重点分析测试过程中CPU和内存是否出现了瓶颈:CPU需要查看其利用率是否经常达到100%或平均利用率一直高居95%以上;内存需要查看是否够用以及测试过程是否存在溢出现象(对于一些中间件服务器要查看其分配的内存是否够用)。

第三步:查看虚拟用户与事务的详细执行情况。

在前两步确定了测试场景的执行情况基本正常后,接下来就要查看虚拟用户与事务的执行情况。对于虚拟用户,主要查看在整个测试过程中是否运行正常,如果有较多用户不能正常运行,则需要重新设计场景或调整用户加载与退出方式再次进行测试。对于事务,重点关注整个过程的事务响应时间是否逐渐变长以及是否存在不能正常执行的事务。

总之,任何用户或事务的执行细节都应该认真分析,不可以轻易忽略。图5-7所示的就是一个性能逐步下降的服务器,需要进一步分析其性能下降的原因,例如查找是否存在内存泄漏问题;图5-8则是一个性能相对稳定的服务器,但是响应时间偏大,这时需要分析程序算法是否存在缺陷或服务器参数的配置是否合理。

图5-7 性能逐步下降的服务器

下面是虚拟用户与事务分析的常用准则: 虚拟用户如有失败,则要查明原因;

在整个测试过程中,所有的虚拟用户是否一直稳定运行并成功执行全部事务。如果仅有一个用户或部分用户能够正常运行,则说明测试脚本可能存在问题;

对于失败的事务首先要分析其失败原因,接着要查看事务的失败是否导致了用户失败; 判断用户是否可以接受事务平均响应时间值以及90%用户的最大响应时间值;

查看整个测试过程的事务平均响应时间是否逐步变大,正常情况下,事务平均响应时间的变化应该是接近于平行X轴的一条直线;

事务响应时间是否在整个测试过程中随着用户的增加而线性变短。正常情况应该是,当一定范围内的用户并发时,事务响应时间应不会有太大变化;

服务器每秒通过的事务总数、某一事务每秒通过数是否稳定,如果整个测试过程基本不变,则要分析是服务器达到了处理上限,还是Generator产生的压力达到了上限;

按照迭代次数来运行的场景,要分析通过的事务总数是否与设定的一致。如果不一致,则可能是测试脚本存在错误,也可能是待测试程序存在功能错误,应该在调整后再次进行测试; ……

图5-8 性能稳定的服务器

Analysis对虚拟用户和事务提供了非常强大的跟踪功能,可以跟踪每一个用户及其相关事务的执行情况。这些内容可以在Analysis菜单“Reports→Crystal Report”下找到。这部分内容将在5.3节介绍。

第四步:查看错误发生情况。

整个测试过程的错误发生情况是分析的重点。下面查看错误发生情况的常用准则: 查看错误发生曲线在整个测试过程中是否有规律变化,如果是,则意味着程序在并发处理方面存在一定的缺陷。如图5-9所示的每秒缺陷数量曲线很有规律,这是因为服务器定期生成缓存文件导致用户不能正常访问而产生的错误;

图5-9 规律变化的每秒错误数曲线图

查看错误分类统计,作为优化系统的参考。例如Web性能测试,当出现瓶颈时往往需要查看服务器的错误统计信息结果:如果“超时错误”达到90%以上,可能需要提高硬件配置;如果有较多的“内部服务器错误”,则可能是程序方面存在问题。

第五步:查看Web资源与细分网页。

本步骤仅适用于Web性能测试。查看Web资源图时,往往需要结合前面对虚拟用户以及事务响应时间的分析结果,重点分析服务器的稳定性。对于网页细分功能则应遵循如下原则:首先分析从用户发出请求到收到第一个缓冲为止,哪些环节比较耗时;其次找出页面中哪些组成部分对用户响应时间影响较大;在页面的性能问题定位后,就可以采取相关的解决方案。

我们将在5.2节对Web资源和网页细分进行更加详细的介绍。 5.1.3 一个视频网站例子 项目背景信息

近两年,随着网络的发展,视频网站如雨后春笋般出现。尤其一些热门的视频网站,拥有巨大的用户群体。如一些知名电视台的宽频网站,在社会发生热点新闻期间的并发用户访问数量会达到百万级以上。巨大的并发访问量对系统的性能提出了非常高的要求。

本案例探讨的是一个已经上线的视频网站遇到的性能问题,该系统的设计目标是每天150万的PV(页面浏览)量。在上线后,由于用户并发量较大,CPU的利用率经常高居100%,导致Oracle数据库发生停止服务的现象。数据库不工作,网站运营人员就无法维护系统,甚至导致终端用户不能正常访问网站。显然,这类问题是不能容忍的。

在探讨性能测试工作之前,先简要介绍这个系统的体系结构,其功能点将在后面的内容中陆续介绍。整个系统由下面两块组成:

视频发布系统:该系统是一个成型的产品,主要功能是建立一个运营平台,把视频发布到系统中,并为门户提供接口。本系统已经有多家成功应用的案例。

网站门户:对视频发布系统进行二次开发后实现的一个应用。借助视频发布平台提供的接口,门户实现了和视频发布系统所维护的运营平台之间的信息交互。网站的用户主要通过门户来欣赏视频。

下面开始探讨如何测试系统的性能问题。 实验室的测试执行与结果分析

对本系统而言,由于已经发现了问题,所以性能测试的目标非常明确,就是要找出导致数据库停止服务的原因。而分析这类问题时,很容易想到两个常见的原因:程序算法上的缺

陷或数据库配置不正确。算法上的缺陷会导致CPU资源过度消耗,而配置不正确也会引起数据库系统运行异常。因此,性能测试设计和实施将围绕这两个目标来进行。 下面详细介绍实验室的性能测试过程。 1. 测试用例设计 由于问题出在数据库上,因此测试用例应该针对数据库来进行。数据库的测试通常会分为下面三个步骤: 首先,把数据库的操作分为Insert 、Update、Delete、Select四种,分别隔离进行测试,并定位哪种操作容易引起问题; 其次,把用户的日常操作模拟出来,也就是把用户对数据库的操作组合起来进行测试; 最后,做一些疲劳强度或大数据量的压力测试,以使问题快速重现。 确定了整体方案后,接下来就要确定测试过程需要模拟哪些用户操作。分析整个系统的结构,可以看出视频发布系统出问题的可能性不大,因为这是一个成型的产品。因此,问题更可能会出现在网站的门户或门户与视频发布系统的接口上。 经过进一步的分析,了解到网站门户用户访问量主要有三个页面: 视频首页:视频首页是导航页面,主要操作有查找热点视频或自己关注的视频、进入二级分类页面、进入播放页面等。 收费播放页面:付费用户播放视频时进入的页面。 免费播放页面:用户播放免费视频时进入的页面。 这三个页面应该是重点测试的对象,用户日常操作组合测试、疲劳强度与大数据量测试都应该针对它们进行。确定了测试内容后,就可以开始性能测试的实施了。 2. 测试实施过程 不难看出,实验室测试是一个紧急的性能测试任务,因此没有时间进行正规的规划与设计,更多的是一种应变测试。为了保证系统的“正常”运行,环境设在了实验室里,配置如表5-1所示。 表5-1 测试硬件配置 数据库服务器 硬件配置 服务器:两台Dell 2850 CPU:Xeon 3.0GB ´ 2 应用服务器 内存:2GB 软件配置 操作系统:企业版Windows 2003(SP1) 数 据 库:Oracle10g 操作系统:企业版Windows 2003(SP1) Web Server:IIS6.0 作者在《Web性能测试实战》一书中曾经讨论过,由于硬件环境的差异,实验室里的调优只能作为上线后的参考。实验室测试比较适合找出由软件自身缺陷引起的性能问题,较容易发现一些算法方面的缺陷。 常规并发用户测试

由于实验室与用户现场的硬件环境差别较大,因此应该先在实验室中进行“预测试”,看看系统在实验室中的性能表现,为后面的测试提供参考。在本案例中,先选择50个并发用户来观察系统的性能表现,压力持续时间为30分钟。

注意:读者碰到类似项目时可以根据系统自身的特点来选择并发用户数量,这里的50只是个参考。

经过测试后,利用Analysis进行分析,得到了如图5-10的测试结果摘要。从图中可以看出:视频首页(Index页面)不能访问,收费播放页面(Play访问)的平均访问时间为181.834秒。

图5-10 50个并发用户的预测试结果

由此不难得出结论:这是一个过于缓慢的系统!需要进行调整后才能正常开展测试工作。 通常情况下,对这类系统进行调整首先应从系统的参数配置或硬件配置入手,然后分析软件自身的原因。这样做的原因是参数配置不正确的错误很容易纠正,也是常见的性能问题,而分析软件本身则是后面测试工作的主要内容。因此,在查看了Oracle的服务器配置后,立刻发现了一个参数配置问题:Oracle数据库的运行模式是“专有服务器模式”!而“共享服务器模式”才是更适合大规模并发的模式。

关于Oracle服务器的运行模式

在专用服务器模式下,用户连接所需要的全部资源在PGA中进行分配。该内存区为指定私有连接,其他进程不能访问。专用连接采用一对一的连接方式,能很快地响应用户的请求。但是,当用户连接过多时,由于要对每一个用户连接分配资源,因此,连接个数受硬件的限制比较大。为了克服这种情况,Oracle提供了共享服务器运行模式,即用一个服务器的进程响应多个用户连接。只要实例一启动,就分配指定数量的服务器进程,所有用户的连接以排队的方式由分配器指定给服务器进程,其他的进程排队等待。只要用户的请求执行完,就会马上断开连接,分配器会把空闲的服务器进程分配给其他排队的进程。

因此,首先要把Oracle的运行模式调整为“共享服务器模式”再进行测试。

分析“Summary”的意义

从上面的内容可以看出,“Summary”的分析结果决定了是否继续进行后面的工作。在本案例中,通过“Summary”看出系统性能非常令人不满意,因此没有必要进行深入分析。正确的做法是立刻采取调优措施,否则测试工作将无法正常开展下去。

独立场景测试 下面是Oracle调整为“共享服务器模式”后的测试实施过程。为了更好地对问题定位,测试只针对播放页面来进行。图5-11是800个用户并发、压力持续30分钟的测试结果综述。

从图5-11中可以看出,“Action”事务即打开播放页面的平均时间是1.354秒,这是非常好的结果。但是应该注意到:半小时内有超过17万个播放页面不能正常打开,同时计算出为“打开播放页面”事务的通过率为82%。82%的事务通过率标志着后续的性能测试任务十分艰巨,而半小时内超过17万个播放页面不能正常打开,是任何视频网站都不能接受的。

为了让问题更好地暴露出来,我们又进行了一次更长时间的场景测试:压力持续时间增加到三倍,即1.5个小时;并发用户增加到了900个。测试结果如图5-12所示。

与图5-11的测试结果相比,得到如下结论:“打开播放页面”事务的平均响应时间稍稍变大,这是用户数量变大的正常反应;事务通过率85%与82%相比没有太大变化。稍稍提高的通过率很可能是由于测试时间较长、打开的部分页面存在于服务器缓存中造成的。

图5-11 共享模式下800个用户的并发测试结果

图5-12 共享模式下900个用户的并发测试结果

这时,打开Oralce的管理工具,借助Oralce提供的工具进行分析。结果发现了五个问题!如图5-13所示。

图5-13 数据库分析的结果

打开图5-13中的“发现SQL语句消耗了大量数据库时间”链接进行查看,发现Oracle找出了三个SQL语句的问题,而这三个语句恰恰是播放页面频繁使用的语句!终于找到了程序本身的一个原因:SQL语句消耗了大量的数据库时间,其他问题极有可能是语句不合理引起的。主要推理如下:当一些反复执行的SQL语句效率过低时,首先会造成高速缓存不够用,随之引起较大的I/O;而频繁的I/O势必会消耗大量的CPU。因此整个系统的瓶颈极有可能是这三个语句引起的。

测试过程中棘手的性能问题

这两次测试结果还有一个奇怪的现象:一方面事务响应时间较快,另一方面却有大量的事务没有响应。仅根据目前的测试结果还看不出直接原因。但这也很可能是前面的三个SQL语句引起的:因为这种现象很像用户直接收到不能访问的通知,所以不再等待服务器的结果反馈,在客户端的表现就是页面无法打开。

这种现象很有可能是耗资源的SQL语句瞬间霸占了全部CPU资源,导致数据库拒绝服务。当调整了SQL语句再次进行测试时,这种现象消失,证明推理是正确的。

再借助Analysis打开图5-14的“事务平均响应时间图”和图5-15的“建立第一个缓冲的时间分解图”,可以得出以下结论:

(1)事务平均响应时间稳定,说明本次测试的性能问题主要在程序自身。如果是服务器存在问题,则长时间的压力测试会导致响应时间越来越长;

(2)“建立第一个缓冲的时间”主要消耗在服务器上,说明对用户请求的处理方式不合理。

图5-14和图5-15进一步证明了SQL语句可能就是造成系统瓶颈的根本原因。

图5-14 事务平均响应时间图

图5-15 建立第一个缓冲的时间分解图

因此,下一步应该先对SQL语句进行优化,然后对系统进行测试。

SQL语句调整后的测试 开发人员优化了SQL语句后,并对900个用户进行并发、持续1.5小时的压力测试,测试结果如图5-16所示。从图中可以看出,调整后系统的性能非常稳定,所有的用户均可以成功打开“视频播放页面”。

图5-16 SQL语句调整后的测试

再借助Analysis打开图5-17所示的事务平均响应时间图,可以看到整个测试过程“打开播放页面”的平均响应时间成平滑水平线,系统的性能非常稳定。

由此可以得出结论:调整SQL语句的策略是正确的。

图5-17 调整后的事务平均响应时间图

与图5-11和图5-12的测试结果相比,有个细节在分析的时候应该注意到,那就是事务评价响应时间变长了!前面两次测试结果的事务平均响应时间是1秒,而本次则是39秒。那么,哪次的测试结果更合理呢?

根据常理,本次测试用的是普通的PC服务器,900个并发用户用1秒的响应时间显然不合理, 39秒才符合实际情况。1秒的事务平均响应时间只是一种假像,是系统存在性能问题造成的。对于“播放页面”的性能,在图5-16中有稳定表现,可以认为测试过关!

到目前为止,本次性能测试的思路是正确的,可以推广到其他功能点的独立或组合性能测试中。

3. 性能调优方案

最后,对整个上线系统提出了以下调优方案: 系统配置方面

(1)把Oracle的运行模式调成“共享服务器模式”;

(2)增加了分配给Oracle的内存:把内存调整成系统内存的55%;

(3)增加共享池(SHARED_POOL)和缓冲区高速缓存(DB_CACHE)的大小; (4)对数据库表和查询相关的字段建立索引; 应用程序方面

(1)用优化后的程序来替换原有程序;

(2)采用页面缓存技术来提高用户访问速度,同时减缓对数据库的压力; 线上的性能测试验证

按照调优方案对线上系统进行调整后,接下来的工作就是和客户一起验证系统的综合性能表现。线上测试与实验室测试不同,不能影响用户的正常使用——不但不能产生垃圾数据,更不能把服务器压垮。

在前面的项目背景中,提到过本系统的设计目标是满足每天150万的PV(页面浏览量),因此,线上测试主要是为了评估服务器能够处理的浏览量。同时,为了保证服务器的稳定运行,在真实线上环境只模拟了500个并发用户。

下面是对线上系统首页的测试结果摘要,主要测试了动态页面无缓存、静态页面缓存方式两种情况:

1. 页面无缓存、500用户并发 测试场景持续执行时间:6分钟 运行的最大用户数: 1000个 测试内容:打开任意视频进行播放 事务平均响应时间如图5-18所示。

图5-18 事务平均响应时间

事务响应时间的详细情况如表5-2所示。 表5-2 事务响应时间(秒) 最小值 0.622 平均值 6.247 最大值 12.338 CPU利用率如图5-19所示。 图5-19 CPU利用率 其中服务器CPU利用率(%)的详细情况如表5-3所示。 表5-3 服务器CPU利用率 Oracle服务器 Web服务器 最小值 0.0 4.688 平均值 82.723 67.543 最大值 95.703 95.833 每秒播放页面下载数如图5-20所示。

图5-20 每秒页面下载数

每秒播放页面下载数的详细表情况如表5-4所示。

表5-4 最小值 0.0 平均值 67.126 最大值 92.25 本次测试结论如下: Oracle服务器CPU的平均利用率为82.723%,说明数据库系统稳定运行。Web服务器的CPU平均利用率是67.543%。如果按一天8小时计算,一台服务器每天的PV均值为:67.126*3600*8≈193万个,足可以支撑150万个PV。 2. 采用静态页面缓存方式、500用户并发 测试场景持续执行时间:6分钟 运行的最大用户数:1000个 测试内容:打开任意视频进行播放 测试过程中的事务平均响应时间如图5-21所示。 图5-21 事务平均响应时间 事务平均响应时间的详细情况如表5-5所示。 表5-5 最小值 0.682 平均值 6.124 最大值 9.682 测试过程中CPU利用率如图5-22所示: 图5-22 CPU利用率 其中服务器CPU利用率(%)的详细情况如表5-6所示。 表5-6 CPU利用率(%)测试结果数据 Oracle服务器 Web服务器 最小值 0.0 3.125 平均值 8.267 67.327 最大值 15.445 89.119 测试过程中每秒播放页面下载数如图5-23所示。 图5-23 每秒页面下载数 每秒播放页面下载数的详细情况如表5-7所示。 表5-7 播放页面下载结果数据 最小值 24.375 平均值 69.043 最大值 96.125 本次测试结论如下: Oracle服务器CPU的平均利用率非常低,为8.267%,这说明静态页面缓存技术大大节省了对数据库的资源消耗,系统更加稳定运行。Web服务器的CPU平均利用率是67.323%。如果按一天8小时计算,一台服务器每天的PV均值为:69.043*3600*8≈199万个,足可以支撑150万个PV。 在满足系统性能测试目标的前提下,Oracle数据库CPU的利用率不足10%,标志着本次性能测试工作圆满完成。 案例总结 通过本项目的性能分析,相信读者更加体会到性能分析是一个相当复杂的过程,仅靠Analysis是远远不够的。在实际工作中,往往会借助各种系统工具以及各方面的综合知识找出系统的瓶颈。例如在本项目中,Oracle自带的管理工具起了至关重要的作用,本案例恰恰是借助它发现了引起系统瓶颈的SQL语句,借助这个突破口逐步解决了其他性能问题。 当进行性能分析与调优时,应该先从容易的地方入手。这样做的原因是可以先排除常见的、容易引起瓶颈的问题,避免各种因素混杂在一起不容易对问题进行定位。例如,本案例就是先从Oracle参数配置入手,逐步深入到应用程序内部。 在实际项目中,还应该要细心地查看Analysis的各种分析报表,不能让任何一个性能问题从“眼皮底下”溜掉。在“抓住了”系统存在问题后,就可以深入地分析下去。

7.1 认识Java虚拟用户

众所周知,Java语言是一门非常适合网络开发的语言,用Java语言进行性能测试脚本开发将会大大提高测试开发效率。LoadRunner中提供了对Java虚拟用户的支持。Java虚拟用户依托于JDK,这大大扩充了LoadRunner的脚本开发功能。测试人员既可以用Java虚拟用户来测试基于Java的应用系统的性能,又可以使用Java语言的强大开发脚本功能来测试其他平台的系统性能。

本章将和读者一起探索LoadRunner的Java世界,重点讨论如何开发Java自定义的虚拟用户脚本。本章主要内容如下:

n 认识Java虚拟用户 n Java脚本开发基础 n Java算法测试案例 7.1 认识Java虚拟用户 7.1.1 Java虚拟用户协议

Java虚拟用户脚本主要有Java Vuser、Corba-Java、RMI-Java、EJB等类型。这些类型的虚拟用户脚本均可以用Java语言来手工编写。下面将对各类Java虚拟用户简单地进行介绍。 Java Vuser

Java Vuser是自定义的Java虚拟用户脚本,脚本中可以使用标准的Java语言。这种虚拟用户不可以录制,只能采用纯手工编写,其适用范围和C Vuser一样,非常广泛。

本章主要探讨Java Vuser的开发方法。 Corba-Java

Corba-Java类型的虚拟用户主要用来测试用Java编写的、使用Corba应用程序或小程序的性能,用户可以先运行VuGen录制的脚本,然后使用标准的Java库函数以及LoadRunner特有的Java方法来增强该脚本。 RMI-Java

RMI-Java 虚拟用户适用于测试RMI(远程方法调用)Java应用程序或小程序。选择RMI-Java 用户进行录制,VuGen可以创建对应的Java脚本。完成录制后,可以使用JDK或自定义类,通过标准的Java代码来增强或修改脚本,还可以通过特定于LoadRunner的Java方法增强该脚本。 EJB

EJB虚拟用户专门用于测试Enterprise Java Beans对象。采用EJB协议,VuGen会自动创建脚本以测试EJB功能,无需录制或编程,这和标准的Java Vuser明显不同。

在使用EJB协议类型的虚拟用户生成脚本前,需要指定JNDI属性和关于应用程序服务器的其他信息。LoadRunner的EJB检测器首先扫描应用程序服务器并确定哪些EJB可用;接着选择要测试的EJB,LoadRunner将生成要测试每个EJB方法的脚本,并为每个方法创建事务,便于度量其性能并找出问题。

需要注意的是,创建EJB虚拟用户测试脚本必须在应用程序服务器主机上安装LoadRunner EJB检测器,而且检测器必须处于活动状态。EJB检测器是一个独立的代理程序,必须将其安装在要扫描查找EJB的每一台计算机上。安装EJB检测器前,计算机上还需要安装有效的JDK环境。

本书中将以Java Vuser为例来讲解Java虚拟用户的开发和使用方法。Corba-Java、RMI-Java、EJB、Jacada Vuser等类型的虚拟用户使用方法可以参考LoadRunner的联机帮助手册,但是其手工脚本的开发方法与Java Vuser是一样的。

在“新建虚拟用户”对话框中的“自定义”或“全部协议”类别下选择“Java Vuser”即可创建空的Java Vuser脚本,如图7-1所示。

在自动生成的脚本中,vuser_init、vuser_end部分没有任何内容,Actions部分生成的脚本如图7-2所示。对于Java类型的虚拟用户,可以编辑Actions类,而init、end部分则不可以进行编辑。在Actions类中,有三个方法init、action和end,通常在init方法中编写初始化代码、在action方法中编写业务流程、在end方法中编写诸如释放资源之类的代码。

图7-1 选择Java Vuser协议

图7-2 Java Vuser自动生成的Actions部分

Java Vuser脚本中可以放置任何标准Java代码,这也是Java虚拟用户的魅力所在。 7.1.2 Java虚拟用户适用范围

LoadRunner完全支持C语言,原则上可以用C语言实现任何想要录制的功能。但是C语言的特点决定了这样做是不现实的:一是性能测试脚本开发成本非常高;二是很多企业的测试人员开发基础不好。因此,性能测试开发多选用C++、C#、Java等面向对象语言——因为这类语言既有成熟的集成开发工具,又有庞大的类库来支撑,测试脚本开发速度会更快。下一章将重点介绍如何用.NET来开发性能测试脚本。

Java虚拟用户适用范围非常广,归纳起来主要有两大类:一类是不太适合录制的业务功能点的性能测试,例如网页上Http文件的下载过程、视频播放等;另一类是基于Java语言开发的应用系统的性能测试,这类应用更适合采用Java虚拟用户进行测试。 用Java Vuser实现无法录制的测试需求

这类测试需求往往关注于对服务器产生的压力,重点测试在一定压力下服务器的负载情况,为评估、部署、调优系统提供参考。在这类测试工作中,Java的作用仅是一门语言,用于辅助测试人员完成测试程序的开发。

在性能测试工作中,不能录制的测试点很多,主要有以下几类: l 含有控件的Web应用

在Web应用中,很多包含一些插件的浏览器应用经常不能录制。这类应用往往涉及很多协议,因此录制起来不是特别方便。

对于这类应用,可以用Java语言调用其功能来完成用户行为的模拟。 l 一些和媒体播放、文件下载等相关的应用

媒体播放或文件下载等过程的录制往往不容易控制,如果是应用基于P2P的协议,则会更加复杂。因此,这类测试只能由测试人员开发虚拟用户脚本来完成。 用Java Vuser测试基于Java语言的应用系统性能

相比前面,基于Java语言开发的应用系统,在性能测试中采用Java虚拟用户更显得“门当户对”。这类虚拟用户主要应用在下面两类测试中: l 核心算法或业务的执行效率

对银行、电信等大型的基于J2EE的架构应用系统中,开发阶段的性能测试是必不可少的。为了降低后期遇到性能问题的风险,往往在开发阶段进行一些核心业务的性能测试。这些核心业务或要求较高的执行效率,或者要求支持较多用户的并发。因此,可以利用Java Vuser来测试这些业务的执行效率。 l Java应用中不能录制的业务模块

很多时候,一些J2EE架构的业务系统用LoadRunner录制起来不是特别方便,例如一些Java智能客户端程序。这时可以手工编写测试代码,完成测试任务。与非Java应用相比,Java应用中不能录制的业务功能更适合采用Java虚拟用户,因为可以直接对一些方法进行调用。

由于Java语言的强大功能,Java虚拟用户的应用远不止上面这些,读者可以慢慢挖掘。 7.1.3 脚本开发环境配置

Java虚拟用户需要Java开发与运行环境的支持,因此首先要安装JDK。JDK是Java开发工具包的简称(Java Development Kit),Sun公司将JDK1.2以后的版本通称为Java 2。JDK的另外一种叫法是J2SDK(Java 2 Software Development Kit),现在比较常见的J2SDK是1.4以上的版本。JDK可以从http://java.sun.com/网站上下载,这里不再赘述。

下面简单介绍Java虚拟用户开发与运行环境的配置。在本章中,JDK安装在“C:\\j2sdk1.4.1”路径下,LoadRunner安装在“D:\\Program Files\\Mercury Interactive\\Mercury LoadRunner”目录下,系统调试环境以Windows XP为例。

第一步:在桌面上右键点击“我的电脑”,进入到“系统属性”设置界面,切换到“高级”选项卡,如图7-3所示。

第二步:点击图7-3中的“环境变量”,进入到环境变量配置界面,如图7-4所示。 第三步:在图7-4的“系统变量”中,选中CLASSPATH,点击编辑,如果没有CLASSPATH,则点击“新建”,同样会进入图7-5所示的界面。

通常安装了LoadRunner等软件的系统会创建CLASSPATH变量。这里需要注意的是,一定要在CLASSPATH变量值的最前面输入当前路径“.”和JDK的类库路径,例如“.;C:\\j2sdk1.4.1\\lib; C:\\j2sdk1.4.1\\jre\\lib;”,否则将可能导致Java虚拟用户的运行错误。

例如:在图7-4中,CLASSPAT变量值是“.;C:\\j2sdk1.4.1\\lib; C:\\j2sdk1.4.1\\jre\\lib;D:\\ Tomcat-5.0\\common\\lib; D:\\Program Files\\Mercury Interactive\\Mercury LoadRunner\\ classes;D:\\ Program Files\\ Mercury Interactive\\Mercury LoadRunner\\lib”。

图7-3 系统属性界面

图7-4 环境变量设置

图7-5 系统类路径设置

第四步:在图7-4的“系统变量”中,选中Path,点击编辑,会进入图7-6所示的界面。在变量值的最前面填上JDK开发工具包的bin目录路径。

图7-6 系统Path设置

这里仍然要在Path变量值的最前面输入当前路径“· ”和JDK的bin目录路径,如“.;C:\\j2sdk1.4.1\\bin;C:\\j2sdk1.4.1\\jre\\bin;”,否则Java虚拟用户可能产生运行错误。

例如在图7-6中,Path变量值是:

“.;C:\\j2sdk1.4.1\\bin;C:\\j2sdk1.4.1\\jre\\bin;D:\\Program Files\\Mercury Interactive\\Mercury LoadRunner\\bin;%SystemRoot%\\system32;%SystemRoot%;%SystemRoot%\\System32\\Wbem; C:\\Program Files\\Intel\\Wireless\\Bin\\”。

开发环境的配置会因操作系统和应用程序的安装路径不同而有所差异,根据实际情况进行相应的修改即可。 7.2 Java脚本开发基础 要想掌握Java虚拟用户的使用,测试脚本开发人员至少需要两方面的技能:一是具有Java语言方面的技能,并熟练使用至少一种Java集成开发工具,例如Eclipse;二是掌握LoadRunner的Java API,Java API是访问Vuser函数的基础,通过LoadRunner的Java API可以在脚本中很容易地创建事务与并发点、获取用户信息等功能。 不难看出,Java虚拟用户要求测试脚本开发人员具有良好的开发基础,否则Java Vuser很难发挥应有的作用。限于篇幅,本节在Java测试开发基础部分,仅对Java语言方面的知识做概要介绍,读者可以通过Java方面的书籍进行学习;在LoadRunner的Java API部分,将会介绍一些相对重要的方法。 7.2.1 Java虚拟用户开发基础 Java语言基础 Java语言博大精深,即使资深的Java开发工程师也未必敢自称精通,测试人员就更不容易成为“高手”了。但是如果仅仅为了满足测试开发,还是很容易快速入门的。表7-1列出了一些进行Java虚拟用户开发的知识点,读者可以自行学习。 表7-1 Java语言基础知识 知识点 详细内容 Java背景知识 ² Java历史及发展、语言特点 ² Java开发环境、程序工作原理 续表 知识点 详细内容 Java语言基础 ² Java数据类型 ² Java运算符与表达式、控制语句 ² Java类定义规范 ² Java数组 ² Java的包 ² 面向对象的概念 ² Java中的类、方法和变量 Java与面向对象² Java名字空间及访问规则 技术 ² Java中的抽象类、接口和程序包 ² 对象的构造方法 ² Java数组 Java中的数据结² 向量 构 ² 字符串 ² I/O流概述 输入/输出处理 ² 字节流 ² 字符流 ² 例外的概念 ² 例外的分类 ² 捕获例外 ² 声明例外 ² 抛出例外 ² 多线程基本概念 ² 创建线程的方式 例外处理(Exception) 多线程 ² 线程的生命周期及控制 ² 线程的调度 ² 多线程的互斥与同步 ² 线程组 ² Java与Internet Java基本网络编² 使用InetAddress 程 ² 使用URL ² Socket通信、数据报通信 JDBC ² JDBC基础、JDBC驱动程序 ² JDBC编程 续表 知识点 详细内容 Java Servlet ² Servlet基本概念 ² Servlet API ² Servlet执行过程与生命周期 ² 容器、部署与运行 Java Server Page² JSP基础、JSP语法 (JSP) 测试人员在掌握了上面这些技能后,基本可以进行Java虚拟用户的开发了。同时,本着学无止境的原则,测试人员仍要以积极的态度来学习Java开发知识。 Java集成开发工具Eclipse Eclipse是一个与NetBeans、Sun ONE Studio和Borland Jbuilder类似的,一种基于Java的整合型可扩展开发平台,也是目前最著名的开源项目之一。IBM一直在大力支持该项目的发展,并在2001年11月宣布投入4千万美元到该项目的研发,这也是Eclipse项目发展较快的原因之一。 Eclipse专注于为高度集成的工具开发提供一个全功能的、具有商业品质的工业平台。它主要由Eclipse项目、Eclipse工具项目和Eclipse技术项目组成,具体包括四个组成部分:Eclipse Platform、JDT、CDT和PDE。JDT支持Java开发、CDT支持C开发、PDE用来支持插件开发,Eclipse Platform则是一个开放的可扩展IDE,提供了一个通用的开发平台。 Eclipse SDK(软件开发包)是Eclipse Platform、JDT和PDE所生产的组件合并,它们可以从eclipse.org网站(http://www.eclipse.org/downloads)上下载。这些组件提供了一个具有丰富特性的开发环境,允许开发者有效地建造可以无缝集成到Eclipse Platform中的工具。Eclipse SDK由Eclipse项目生产的工具和来自其他开放源代码的第三方软件组合而成。 在接下来的内容里,将介绍用Eclipse创建与编译Java类文件的过程以及如何在Java虚拟用户中调用编译好的类文件。关于Eclipse更深入的内容请读者参考其相关书籍。 l 创建Java项目 启动Eclipse后,依次选择菜单的“文件”→“新建”→“项目”,进入图7-7所示的界面。

图7-7 新建项目

在图7-7中选择Java项目,单击“下一步”,进入图7-8所示的界面。

图7-8 配置项目

在图7-8的“项目名称”输入“LoadRunnerDev”,“位置”下选择“在外部位置创建项目”,目录下选择“D:\\LoadRunner”。然后点击“配置缺省值(O)...”,进入图7-9所示的界面。

图7-9 配置缺省值

在图7-9的“源和输出文件夹”处选择“文件夹(F)”,“源文件夹名(S):”默认为“src”,无须改变,在“输出文件夹名(O):”处输入“classes”,完成后返回图7-8所示界面。在图7-8中点击“下一步”,进入图7-10所示的界面。

图7-10 配置结果界面

在图7-10中,单击“完成”,新建Java项目工作完成。在图7-11中可以看到新建的项目“LoadRunnerDev”。

图7-11 包资源管理器

l 建立Java文件

如图7-12所示,在LoadRunnerDer中选中“src”,点击鼠标右键,进入“新建”,点击“类”,进入图7-13所示的新建Java类文件设置界面。

图7-12 新建Java类文件

在图7-13中,输入包名称“com.lr.test”、类名称“HelloWord”,其他各项设置默认即可。点击“完成”,进入源代码编辑界面,如图7-14所示。

图7-13 文件基本配置信息

图7-14 文件编辑界面

在源代码编辑界面,输入测试语句“System.out.println(\"HelloWord!\");”如图7-14所示。代码编辑完成后,按照图7-15编译与运行程序。如果编译执行成功,可以在Eclipse的控制台看到输出结果,如图7-16所示。

图7-15 执行Java程序

图7-16 查看编译结果

l 编译与运行Java程序

编译后的class文件可以在“D:\\LoadRunner\\classes\\com\\lr\est”下找到,如图7-16所示。需要注意的是,类文件的存放路径是根据建立项目的设置来决定的。 l 开发虚拟用户脚本

进入到“D:\\LoadRunner\\classes”目录下,把整个“com”文件夹复制到“C:\\j2sdk1.4.1\\lib”,这样LoadRunner创建的所有Java Vuer脚本均可以直接调用;如果放到虚拟用户脚本的当前路径下,则只有当前的虚拟用户脚本可以调用。多台主机进行并发测试时,应该把编译好的类文件放到对应的虚拟用户脚本目录下。根据图7-1和图7-2所示新建一个Java虚拟用

户脚本,并对照图7-17,输入同样的测试脚本,尤其要在代码开始处输入包的导入语句“import com.lr.test.*;”。

图7-17 Java虚拟用户示例脚本

l 运行虚拟用户脚本

点击Virtual User Generator 工具栏上的

正确的执行结果如图 图标开始执行脚本。

7-18所示,可以看到执行日志中有“System.out: HelloWord!”。如果运行不正确,读者可以参照前面的过程进行分析,同时检查JDK的路径设置。

图7-18 正确执行的虚拟用户脚本

与 C Vuser脚本相比,Java Vuser 脚本是先编译再执行,而C Vuser脚本是解释性的。VuGen在JDK安装路径内查找javac编译器,并在脚本内编译Java代码。该阶段由VuGen窗口底部的“正在编译... ”状态消息来指示。如果在编译期间出错,则这些错误将在执行日志中列出。

测试脚本保存后,接下来可以放到Controller中来运行,读者可以自己进行实验。需要注意的是,如果进行多台计算机联机测试,则所有运行测试脚本的客户机必须安装JDK环境并正确设置路径,否则将会出现如图7-19所示的提示。

图7-19 JDK环境不正确的提示

要想正确使用Java虚拟用户,首先要保证测试环境配置正确。执行测试时,可以先用本节的示例程序来检查环境是否正确,然后再进行复杂功能的开发。 7.2.2 LoadRunner的Java API

LoadRunner为访问Vuser函数提供了特定的Java API,这些函数都是lrapi.lr类的静态方法。借助Java API可以大大增强Java虚拟用户脚本的可用性。本节将介绍常用的Java API的用法,更多的函数及其用法读者可以参考LoadRunner联机手册。

在Java虚拟用户中,Java API函数的用法与Vuser函数中的用法基本一致,只是写法稍稍不同,更符合Java语言的特点。 1. 事务函数(Transaction Functions)

(1)int lr.start_transaction( String transaction_name ):标记事务开始;

(2)int lr.end_transaction ( String transaction_name, int status ):标记事务结束。 2. 信息函数(Informational Functions)

(1)String lr.get_group_name( ):返回 Vuser 组的名称;

(2)String lr.get_host_name( ):返回执行 Vuser 脚本的负载生成器的名称; (3)String lr.get_master_host_name ( ):返回运行Controller计算机的名称; (4)int lr.get_scenario_id( ): 返回当前方案的ID; (5)int lr.get_vuser_id( ) :返回当前 Vuser 的ID。 3. 运行时函数(Run-Time Functions)

(1)void lr.peek_events ( );:指示可以暂停Vuser 脚本的位置;

(2)int lr.rendezvous( String rendezvous_name ):在 Vuser 脚本中设置集合点;

(3)void lr.think_time( double time ):暂停脚本执行,模拟实际用户操作之间的思考时间。

4. 字符串函数(String Functions)

(1)String lr.eval_string ( String instring ):用当前值替换参数; (2)int lr.eval_int ( String name ):用整型值替换参数;

(3)int lr.next_row ( String dat_file ):指示使用指定参数的下一行数据。 5. 消息函数(Message Functions)

(1)int lr.debug_message( int message_level, String message):向输出窗口发送测试过程的调试消息;

(2)int lr.error_message ( String message ):向Vuser日志文件和输出窗口发送错误消息以及位置的详细信息;

(3)int lr.log_message ( String message ):向 Vuser 日志文件发送消息; (4)int lr.message ( String message ):向输出窗口发送消息;

(5)int lr.output_message ( String message ):向日志文件和输出窗口发送消息和位置信息;

(6)int lr.vuser_status_message ( String message ):向Controller窗口中的“Vuser状态”区域发送消息。 7.3 Java算法测试案例

本节将结合一个具体案例来讲解如何借助Java Vuser来测试Java程序的算法。在案例中,主要模拟了测试某银行的信用卡审批过程,这部分内容是开发阶段性能测试的一部分。在这个测试例子中,主要发现了在并发时的两个算法问题:提交任务处理结果发生异常时Socket没有正常关闭;申请任务方法giveOutWork()没有加同步控制关键字synchronized。

为了更好地演示测试效果,程序中忽略了实际程序中的一些细节,例如具体的任务申请以及处理过程。 测试内容简介

信用卡审批程序主要包括两个部分,即客户端程序与服务器端程序。客户端程序包含一个Client.java类文件,即仅包含一个类Client,主要封装客户端的“申请—处理—提交”操作。服务器端程序即WorkServer.java,包含WorkQueue、AcceptClientThread、WorkServer三

类。类WorkQueue主要完成任务队列的构建与管理工作;类AcceptClientThread继承线程类Thead,以独立线程的方式来处理客户端申请任务并保存客户端对任务的处理结果;类WorkServer是服务器端的执行类,主要完成对WorkQueue、AcceptClientThread的调用。

下面具体介绍业务流程。客户端对一项任务的业务流程如下:

第一步:与服务器建立连接,向服务器发出处理任务申请,等待服务器返回任务; 第二步:从服务器得到任务后,开始进行处理;

第三步:处理完毕后,提交结果给服务器进行保存,然后等待服务器返回结果; 第四步:输出服务器的保存结果; 第五步:结束当前的任务处理。

客户端源程序清单:Clien.java package com.loadrunner.test; import java.io.*; import java.net.*; /**

* 客户端{申请任务、确认是否可以审批、处理、传递结果得到确认} * @author ChenShaoying */

public class Client { Socket socket; int clientNumber;

BufferedReader is;//读出服务器返回的输入流 PrintWriter os;//反馈给服务器的输出流 /**

* 向服务器申请任务 */

Client(Socket s) { try {

this.socket = s;

this.is = new BufferedReader(new InputStreamReader(s .getInputStream()));

this.os = new PrintWriter(s.getOutputStream()); this.clientNumber = Integer.parseInt(is.readLine()); } catch (Exception e) {

System.err.println(\"Error:Can not init the network!\"); } }

public int applyWork() { int workNumber=-1; try {

this.os.println(\"Apply\");//发出申请 os.flush();

workNumber = Integer.parseInt(this.is.readLine());//读出申请结果 if (workNumber == -1) {

System.out.println(\"Server has no Work to do\"); System.exit(1);//退出程序 }

} catch (Exception e) {

System.err.println(\"Error:Can not apply the network!\"); }

return workNumber; } /**

* 处理任务:添加实际处理过程即可,本处略 * @return deal with result

* @author ChenShaoying */

public int dealWithWork(int worknumber) {

System.out.println(\"dealWithWork:\"+worknumber); return 1; } /**

* 传递结果到服务器确认 * @return ensure result * @author ChenShaoying */

public boolean finishWork(int workNumber) { boolean finish=false; try {

this.os.println(\"finish\"); os.flush();

finish = Boolean.valueOf(this.is.readLine()).booleanValue(); if (finish == false) {

System.out.println(\"Error:Work finish can not be set!\"); System.exit(1); }

} catch (Exception e) {

System.err.println(\"Error:Can not start the network!\"); System.exit(1); }

return finish;

} }

服务器端对一项任务的业务流程如下:

第一步:建立任务队列,等待审批人员进行申请; 第二步:服务器收到用户申请后,系统会先锁定记录; 第三步:修改当前记录状态,并把当前任务返回给客户端; 第四步:等待客户端审批人员返回处理结果;

第五步:收到客户端提交的处理结果后,保存处理结果。 服务器端源代码清单:WorkServer.java package com.loadrunner.test; import java.io.*; import java.net.*; /**

* 队列{原始N个任务,接受申请返回任务号,检查任务是否正在处理、接受审批任务确认} * @author ChenShaoying */

class WorkQueue{

private int []WorkFlag;//0-未申请;1-申请后正在处理;2-处理完成 private int total; int nowNumber;

//创建任务队列:total-队列长度;WorkFlag-用来监控队列中每个任务状态的数组;nowNumber-当前可以申请到的任务编号 WorkQueue(int totalNumber) {

this.total=totalNumber;

this.WorkFlag=new int [this.total]; for(int i=0;i{

this.WorkFlag[i]=0; }

this.nowNumber=1; }

//接受客户端申请,把队列任务提供给当前申请的客户端 int giveOutWork() {

int k=this.nowNumber;

this.WorkFlag[this.nowNumber]=1; try {

Thread.sleep(1);//模拟服务器对任务的处理时间 } catch (InterruptedException e) { e.printStackTrace(); }

this.nowNumber++; return k; }

//如果当前任务的状态是正在处理,则修改其状态为完成并返回true,否则返回false。 boolean finishWork(int worknumber) {

int number=worknumber; if (this.WorkFlag[number]==1)

{

this.WorkFlag[number]=2; return true; }else{

System.err.println(\"Work \"+number+\" Can not be finished\"); }

return false; } } /**

* 客户端连接对话线程{接受任务申请返回任务号、接受审批任务确认、接受任务处理结果、返回确认消息} * */

class AcceptClientThread extends Thread {

private Socket socket=null; private int clientNumber; private WorkQueue workQueue;

AcceptClientThread(Socket socket,WorkQueue q,int clientNumber) {

this.socket=socket;

this.workQueue=q;//初始化对任务队列的管理 this.clientNumber=clientNumber; }

int giveOutWork()//分配任务 {

try{

sleep(100);//延迟100毫秒分派,用于模拟实际工作中分发前的准备工作 }catch(Exception e) {

System.err.println(e); System.exit(0); }

return workQueue.giveOutWork(); }

boolean finishWork(int worknumber) //结束工作 {

return workQueue.finishWork(worknumber); }

public void run() { try{

//创建输入输出流 BufferedReader is=new

BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter os=new PrintWriter(socket.getOutputStream()); os.println(this.clientNumber); os.flush();

//1.接受任务申请返回任务号 String step=is.readLine(); while(step.equals(\"Apply\")==false) {

sleep((int)Math.random()*100); step=is.readLine(); }

int worknumber=this.giveOutWork(); os.println(worknumber);//任务号返回给客户端 os.flush();

//2.任务处理完毕后,把处理结果返回给服务器 step=is.readLine();

while(step.equals(\"finish\")==false) {

sleep(100); step=is.readLine(); }

//3.返回确认消息,开始提交客户端的处理结果, //如果没有被处理过(状态为1),则可以提交客户端的结果 boolean result=this.finishWork(worknumber); os.println(result); os.flush(); if(result==true) {

System.out.println(\"Work \"+Integer.toString(worknumber) +\"done by client \"+Integer.toString(this.clientNumber)+ \".\"); }

//关闭连接和输入输出流 os.close(); is.close();

socket.close(); }

catch(Exception e) {

System.err.println(e); } } }

public class WorkServer {

public static void main(String[] args) { // TODO Auto-generated method stub ServerSocket serverSocket = null; boolean listening = true;

WorkQueue queue=new WorkQueue(200000); //创建一个端口监听 try {

serverSocket = new ServerSocket(8000); }

catch (IOException e) {

System.err.println(\"Could not listen on port: 8000.\"); System.exit(-1); } try {

int clientnumber=0; while (listening) {

Socket socket=new Socket();

socket = serverSocket.accept(); //程序将在此等候客户端的连接 clientnumber++;

//客户申请后将启动一个独立线程来处理客户申请

new AcceptClientThread(socket,queue,clientnumber).start(); }

serverSocket.close(); }

catch(Exception e) {

System.err.println(e); //System.exit(-1); } } }

测试源程序

测试思路很简单,主要是模拟多个客户端并发申请与处理任务,因此采用了手工Java虚拟用户。为了方便程序开发,测试程序Test.java先在Eclipse中开发完成。在Test.java类文件中,编写具体的测试执行类Test,用于调用Client.java中的方法。

下面是测试程序Test.java的程序清单: 测试程序清单:Test.java package com.loadrunner.test; import java.io.IOException; import java.net.Socket;

import java.net.UnknownHostException; public class Test {

public void ApplyProccess() throws IOException

{

Socket clientSocket = null; try {

//建立服务器连接,创建输入输出流

clientSocket = new Socket(\"127.0.0.1\ Client client=new Client(clientSocket); //1申请任务号

int worknumber=client.applyWork(); //2处理记录

int result=client.dealWithWork(worknumber); //3发送处理结果到服务器确认

boolean ensureResult=client.finishWork(worknumber); if(ensureResult!=true) {

System.err.println(\"Error:Work check error!\"); System.exit(0); } else {

System.out.println(\"Finish work No.\" +Integer.toString(worknumber)); }

} catch (UnknownHostException e) {

System.err.println(\"Don't know about host: 127.0.0.1.\"); System.exit(1); } catch (IOException e) {

System.err.println(\"Couldn't get I/O for the connection to: 127.0.0.1.\"+e);

System.exit(1); }

//关闭服务器连接 clientSocket.close(); } }

虚拟用户脚本

上面三个程序在Elipse中编译完成后,将会按照类文件的包名称“com.loadrunner.test”生成对应的目录结构“com\\loadrunner\est”,下面可以看到编译后的class文件。

启动VuGen,先创建空的虚拟用户脚本“SimpleJava”,然后把程序的编译结果放到虚拟用户脚本目录下,如图7-20所示。

图7-20 虚拟用户脚本结构

上面的工作完成后,接下来需要修改脚本,以调用Test类中的Test()方法。修改后的脚本如图7-21所示。

在Eclipse中运行WorkServer.java,启动WorkServer服务器后才可以调试脚本。在VuGen中运行脚本,如果在运行结果Log中看到“Finish work No.*”,则表示脚本运行正确,可以成功申请并处理任务。图7-22所示为成功申请并处理了1号任务。

图7-21 修改后的脚本

图7-22 成功处理任务后的运行结果

创建与执行场景

虚拟用户脚本通过调试后,接下来要放到Controller中创建场景。首先运行一个用户,以在Controller中验证脚本的正确性。把脚本迭代次数设置为200,部分运行结果如图7-23所示,说明脚本在Controller中运行正常。

把并发用户变为10个,运行场景,并发申请任务开始发生错误:图7-24是场景运行状态;图7-25是WorkServer运行结果。从服务器上的提示可以看出,Socket连接发生错误。如果没有正常关闭,则会有“

”异常。

图7-23 单用户成功处理任务后的运行结果

图7-24 10个用户并发时的场景状态

图7-25 用户并发时的WorkServer状态

分析这个错误的具体原因很容易,Socket连接发生重置多是由于非正常关闭Socket所致。浏览一下Test.java可以看到程序中有很多System.exit()语句,这种语句会导致直接退出程序而没有执行最后的语句clientSocket.close()。当任务处理过程发生异常时,无疑会导致Socket连接没有正常关闭。解决的方法很简单,在System.exit()语句前加上clientSocket.close()即可。

修正Socket连接缺陷后,10个用户并发时的WorkServer运行信息如图7-26所示,可以看到服务器不能正常提交处理结果。

图7-26 成功处理任务后的运行结果

为了详细追踪问题,需要更改测试程序以及服务器程序。Java虚拟用户脚本需要输出一些信息到控制台,而WorkServer则需要输出不能提交保存结果的任务状态。

新的虚拟用户Actions部分的程序清单如下: package com.loadrunner.test; import lrapi.lr;

import java.io.IOException; import java.net.Socket;

import java.net.UnknownHostException; public class Actions {

public int init() { return 0; }//end of init public int action() { try {

lr.rendezvous(\"申请任务\"); this.ApplyProccess(); }

catch (java.io.IOException e) { e.printStackTrace(); } return 0; }//end of action public int end() { return 0; }//end of end

public void ApplyProccess() throws IOException {

Socket clientSocket = null;

try { //建立服务器连接,创建输入输出流 clientSocket = new Socket(\"127.0.0.1\ Client client=new Client(clientSocket); //1申请任务号

int worknumber=client.applyWork(); //2处理记录

int result=client.dealWithWork(worknumber); //3发送处理结果到服务器确认

boolean ensureResult=client.finishWork(worknumber); if(ensureResult!=true) {

lr.error_message(\"Error:Work \"+worknumber+\"finish error!\");

//System.err.println(\"Error:Work check error!\");

//clientSocket.close();

// System.exit(1); } else {

System.out.println(\"Finish work No.\"+Integer.toString (worknumber)); }

} catch (UnknownHostException e) {

System.err.println(\"Don't know about host: 127.0.0.1.\");

} catch (IOException e) {

System.err.println(\"Couldn't get I/O for the connection to: 127.0.0.1.\"+

e);

}

//关闭服务器连接 clientSocket.close(); } }

程序中用“lr.error_message(\"Error:Work \"+worknumber+\"finish error!\");”语句替换了“System.err.println(\"Error:Work check error!\");”,目的是向Controller的控制台发出消息。

WorkServer类中则修改了finishWork(int worknumber)方法,把其中的“System.err. println(\"Work\"+number+\"Can not be finished\");”替换成“System.err.println(\"Work \"+number+\" Can not be finished,\"+\"WorkFlag is \"+WorkFlag[number]);”,以查找不能保存处理结果的当前状态任务。修改后的程序如下:

再次选择10个用户并发, Controller将会弹出一些错误提示,如图7-27所示。

图7-27 Controller运行时捕获的一些错误

WorkServer服务器弹出的消息如图7-28所示,可以看出不能提交处理结果的任务的状态标志为2,表示已经由其他用户处理完毕,因此提交发生错误。

通过客户端以及服务器的错误信息,基本可以断定任务分配存在重复现象——只有把同一任务分给多个客户端进行处理,才会发生不能提交保存结果的状况。这时自然会想到giveOutWork()方法可能存在问题。检查giveOutWork()方法,发现根本没有做并发同步控制!

图7-28 WorkServer运行结果日志

修正后的giveOutWork()方法如下所示,加了同步关键字synchronized。 synchronized int giveOutWork() {

int k=this.nowNumber;

this.WorkFlag[this.nowNumber]=1; try {

Thread.sleep(1);//模拟服务器对任务的处理时间 } catch (InterruptedException e) { e.printStackTrace(); }

this.nowNumber++; return k;

}

再次运行并发场景,则可以看到任务处理过程完全正确,图7-29即为添加同步控制后的WorkServer运行日志。

图7-29 添加同步控制后的WorkServer运行日志

至此,已经完成了对算法测试以及缺陷修正工作。

本节案例中的程序缺陷看似很容易发现,但在实际项目中是在测试一段时间后才发现并发分配算法存在问题的。读者可以把giveOutWork()方法中模拟服务器对任务的处理时间即Thread.sleep(1)语句注释后再进行并发测试,这时几乎很难再现前面问题,尽管把同一任务分给多个用户进行处理的缺陷仍然存在。

调整后的giveOutWork()方法如下: int giveOutWork() {

int k=this.nowNumber;

this.WorkFlag[this.nowNumber]=1; /* try {

Thread.sleep(1);//模拟服务器对任务的处理时间 } catch (InterruptedException e) { e.printStackTrace(); }*/

this.nowNumber++; return k; }

通过本案例可以看出,很多算法需要认真全面的测试才可以挖出隐藏很深的缺陷。 7.4 本章小结

Java虚拟用户是LoadRunner功能很强的一类虚拟用户脚本。Java Vuser借助Java语言平台的强大功能,既可以测试基于Java平台的应用系统性能(尤其是C/S架构的Java应用),又可以开发一些不容易录制的测试脚本来产生压力。

本章详细介绍了Java虚拟用户的开发方法。首先介绍了Java虚拟用户的适用范围和环境配置;然后介绍了Java虚拟用户脚本的开发基础知识;最后结合Java算法测试案例来讲解如何在实际中使用Java虚拟用户。本章重点讲解了手工Java虚拟用户,其他类型的Java虚拟用户需要读者自己去钻研。

此外,开发Java虚拟用户脚本还需要具有一定的Java开发能力。因此,要想真正用好Java虚拟用户,还需要积极地探索与学习!

因篇幅问题不能全部显示,请点此查看更多更全内容

Top