性能测试是一个入门大略,但是精通难,很依赖实践履历的技能活。
如何编写压测脚本只是小术,而如何快速找到问题的缘故原由,压出瓶颈却是大有学问。
不过本文先从术入手,先对一个自己临时写的的一个网站进行压测,希望能帮大家更好理解性能测试产品,特殊是脚本编写的部分。

开始压测第一件事情绝对不是直接动手就写压测脚本。
一个规范的性能测试须要包括需求调研、测试准备、实行压测、天生压测结果并做汇总几个部分。
这些步骤都有其存在的意义,担保我们压测不会跑偏,这里针对详细的case我们剖析下(注:本文涉及的机器会在本文发布前开释,干系要求地址不再可用,大家就不要压文中的地址了)。

压测之前

jsp购物车库存校验机能测试剧本的编写和调试 CSS

需求调研

这一步我们须要先知道自己要压的系统的情形。
须要根据实际的项目情形进行需求调研。

项目背景

这是一个很大略的测试系统,功能上涉及的紧张是主页浏览、一个登录功能和一个登录后的一个大略单纯下单操作。

项目目标

这次我紧张是希望压出这个网站里的首页(静态页面)、登录、下单3个页面能承载的最大TPS,我会利用不同的并发去压,只为了探求处理能力的上限。
如果是实际的场景里,大家很可能是被问的是,xx个用户能不能顶的住。
这时候可以通过这里来估算。
算出并发数后,根据这些并发数压测后的相应韶光、成功率等指标是否达到预期来判断软件是否知足哀求。

项目范围

这个网站搭建在我刚购买的机器(公网:120.55.240.49/内网:10.47.121.62)上。
上面搭建了个Tomcat,跑了个通过war包打出的大略java web运用。
本次压测紧张涉及主页(http://120.55.240.49:8080/demo/ )、登录页面(http://120.55.240.49:8080/demo/login.jsp )和购买页面(http://120.55.240.49:8080/demo/buy.jsp )。
个中购买页面是须要登录成功后才能下单的,否则会302回登录界面。

软件架构

ECS上安装Tomcat,支配的一个大略Java运用。
个中登录须要用账号密码去查询数据库的用户表,目前表里就初始化了一个admin/123作为登录账号。
购买页面的下单操作也会往数据库里写一条记录。
这里只用了一台ECS,没有利用负载均衡。
总体而言,是一个大略的一台ECS+一个RDS的运用。

这次压测没有分生产系统和测试环境。
不过在实际场景里,须要注明生产环境和测试的环境的差异,并在压测的过程中加以把稳。

当前系统里只有少量几条测试数据,以是数据库查询的话,理论上不会有数据库慢查询(实际上这次也就压测涉及的数据库查询只有登录的时候会查用户表,而用户表目前只有一条记录)。
而关于写入,目前没有在表上做索引。
实际事情中,不仅须要考虑到系统确当前数据量,还须要顾及未来2-3年的数据量情形,以免往后数据量增加的时候负载跟不上。

硬件准备是否充分。
这里可以先评估的是峰值的网络带宽。
CPU、内存紧张是须要根据压测的结果进行评估,但是带宽可以根据预先估算的TPS乘以每个要求涉及的文件的大小来估算。
我这里是压瓶颈,转头看下瓶颈是不是在带宽上。

性能指标

紧张涉及网站预期的性能指标,比如TPS、相应韶光、成功率、压测过程中的涉及的ECS/RDS的负载。
我这里就想看看它能“走多远”,先不设置TPS的指标。
但是相应韶光,我希望首页、登录、下单的相应韶光能在2秒内,要求成功率在99.9%,在压测的过程中ECS的各项指标低于80%,数据库的资源利用率低于60%。
如前面提到的,设置指标的时候最好能考虑到未来2-3年的情形,至少要考虑到近期的峰值(比如接下来是否会有大匆匆)的性能哀求。

业务描述

涉及主页、登录、下单3个页面。

主页包括1个html1个css1个图片。

登录页面通过post要求提交。
如果账号密码错会302回到登录页面。
如果是登录成功,会跳到成功页面提示处理成功。
为联机操作。

下单页面也一样通过post要求提交当前购买的商品和数量。
做事器会判断当前session里的用户信息,如果取不到判断为没登录状态,会302跳到登录界面。
下单的逻辑很大略,没有对库存做校验,只是增加一条记录。
也是联机操作。

本系统不涉及跑批作业,也不涉及其他外部系统。

业务描述

我也没编出来: ) 不过大家实际利用中,须要把稳用户的行为办法,比如做事工具,他们的利用均值、高峰如何,一样平常都是如何利用系统的。
这对付脚本编写逻辑和压测的目标的设置有非常主要的参考意义。

测试准备

测试准备紧张包括测试环境的配置、测试内容的梳理和测试策略的设定。

测试环境

本例子没有分测试环境/线上环境,直接就开始压了。
真实的压测例子里,须要记录生产环境和测试环境在系统架构图、支配图、硬件配置、软件环境,并剖析其差异。
这里我就例子里的被压系统做下记录:

系统架构图用的是serverlet+jdbc直接连mysql,没有连接池,或者诸如ssh、Ibatis等常用框架。
由于太大略这里就不画图了。

支配情形为一台ECS上安装了tomcat 8,然后直接拷上war包完事。
mysql的数据用的是之前调试代码里就创好的表,没有走平时的上线流程之类的。

硬件配置为:

ECS的配置为华东1区域的2核4G I/O优化实例,利用操作系统为CentOS 6.8 64位。
公网带宽购买时设置为5Mbps(峰值)。

RDS的配置为1核2G通用型MySQL 5.6。
能达到的最大IOPS为1000,最大连接数为600。

软件环境上为Java 8,Tomcat没有调JVM参数,没有调过其他参数。

测试内容

本例子先只涉及单交易负载测试,基准测试、稠浊场景下的测试先暂时不考虑。
须要压测的页面为:

模块涉及的要求参数条件条件首页http://120.55.240.49:8080/demo/无无登录http://120.55.240.49:8080/demo/login.jspuserName=admin&password=123无下单http://120.55.240.49:8080/demo/buy.jspgoods=g02&count=1登录

测试策略

我们会先调试通过后,先用1-5个并发担保压测能跑起来,然后逐渐调度并发用户数,每次调度后勾留至少30秒不雅观察做事器的负载和数据库的负载,以及诸如TPS、相应韶光的性能指标。
在不雅观察到做事器的TPS达到瓶颈或者负载达到上限后停滞压测,认为做事的处理能力已经达到。
全体压力过程中的并发数是人为根据当时的情形动态调度的。
这里不要起来便是几千一万的压力去压,否则一样平常情形下,除了把做事器压挂掉外别的什么都解释不了。

关于监控模型,我们配置ECS、RDS为监控工具。
不过由于性能测试的监控数据有延迟,ECS为1分钟,RDS为5分钟,以是在压测的过程中,会登录到ECS上,利用TOP命令来不雅观察更加实时的ECS负载,并登录到RDS的DMS上利用实时性能功能不雅观察RDS的负载。

首页

首页是一个大略的静态页面,这里紧张是展示一下如何利用性能测试产品供应的脚本录制工具的利用方法。

脚本剖析

产生的脚本为(第一次建议先只看注释不看代码,便是#之后的)

#! /usr/bin/env python # -- coding: utf-8 --# PTS Script record tool v0.2.6.4# PTS脚本SDK:框架API、常用HTTP要求/相应处理APIfrom util import PTSfrom HTTPClient import NVPairfrom HTTPClient import Cookiefrom HTTPClient import HTTPRequestfrom HTTPClient import CookieModule# 脚本初始化段,可以设置压测引擎的常用HTTP属性#PTS.HttpUtilities.setKeepAlive(False)#PTS.HttpUtilities.setUrlEncoding('GBK')#PTS.HttpUtilities.setFollowRedirects(False)#PTS.HttpUtilities.setUseCookieModule(False)PTS.HttpUtilities.setUseContentEncoding(True)PTS.HttpUtilities.setUseTransferEncoding(True)## 如想通过ECS内网IP进行压测,必须不才方“innerIp”备注行中输入ECS内网IP,如有多个请以英文逗号分隔,例如:127.0.0.1,127.0.0.2# innerIp:## 脚本实行单元类,每个VU/压测线程会创建一个TestRunner实例工具class TestRunner:

可以看到函数里须要把稳的是def __init__(self)做初始化,这里暂时不涉及,后面会提到。
初始化后压测做事会多次调用def __call__(self)
末了调用一次__del__(self)扫尾。

脚本调试

在保存按钮边上有个调试按钮,点击后可以看到调试的结果。

在脚本调试的过程中,每个要求的内容,相应内容一览无余。
实行日志里还有供应压测的过程中的日志。
如果中间有自己打印了一些日志,也可以在这里看到。
关于日志打印的功能后面也会实践里提到。

压测过程

保存了脚本后,去创建一个压测场景:

把这个场景运行起来。
看到并发很低,从性能测试产品上可以看到性能参数图:

同时比拟一下ECS的负载指标:

看到ECS的CPU根本没用掉。
通过TOP命令看到的CPU、内存的利用情形也是如此。
同时我还用iftop -i eth1看了下公网网卡的流量情形,和监控上看到的一样,公网带宽被打满了

结果总结

从压测结果可以看到,瓶颈在公网带宽上。
由于ECS购买的公网带宽比较小,而首页的静态文件比较大(图片比较大),可以考虑在够用的情形下减少图片的分辨率减少图片的大小。
其余可以做到动静分离,一些静态文件就放到工具存储OSS上面,再合营CDN就完美了。
压测的时候也就不须要在压测这些已经放在OSS/CDN的文件。

登录功能

同样的登录功能也是脚本录制出来的。
这里就不重复解释。
由于后面的登录后下单的这个例子包括登录的所有功能点,这里登录就先跳过。

下单功能

下单是本次测试的最繁芜的一个模块。
首先,下单前须要登录,但是我们这次只是为了测试下单的工单,所有所有的要求,我们希望只登录一次(实际上如果是用了多台施压机,脚本里写一次登录,实际上是每台机器一次,一共登录会被实行多次)。
根据前面讲的脚本的组成逻辑,我们须要把登录写在def __init__(self)里。
除了登录,我们还希望测试每次下单购买的是不同的商品和数量,这时候须要用到参数文件。
其余由于我们这次是希望压测下单的过程中ECS和数据库的压力,对付之前的瓶颈公网带宽,我们假设已经通过动静分离办理了,以是在这里压测脚本里我们不涉及静态资源,走内网压测。
还有我们希望在这个例子里对脚本代码做一次调试,以是须要做一些日志打印。

脚本录制

登录功能可以在录制的时候直接录制好:

先双击上面的初始化,然后录制登录功能。

登录录制好了后,双击事物然后开始录制下单页面。

末了得到的压测脚本如下:

#! /usr/bin/env python # -- coding: utf-8 --# PTS Script Version 1.0# PTS脚本SDK:框架API、常用HTTP要求/相应处理APIfrom util import PTSfrom HTTPClient import NVPairfrom HTTPClient import Cookiefrom HTTPClient import HTTPRequestfrom HTTPClient import CookieModule# 脚本初始化段,可以设置压测引擎的常用HTTP属性#PTS.HttpUtilities.setKeepAlive(False)#PTS.HttpUtilities.setUrlEncoding('GBK')#PTS.HttpUtilities.setFollowRedirects(False)#PTS.HttpUtilities.setUseCookieModule(False)PTS.HttpUtilities.setUseContentEncoding(True)

剖析下这个脚本,特殊把稳__init__(self)里调用了self.init1(),做了登录,然后做了cookies的设置。
其他地方同前一个脚本基本一样。

调试一下,结果还可以。

检讨点

检讨点用于检讨要求的返回内容是否符合预期。
有的时候,我们针对失落败不会直接报3xx乃至是4xx或者5xx,而是返回200,但是相应的内容里提示报错信息。
只有返回的内容是200而且内容是success!我们才认为这个下单操作是成功的。
于是我们修正action1,增加检讨点的功能,修正为

## action1

内网压测

我们修正脚本界面,把所有的公网ip换成内网ip。
以前版本的PTS还是须要设置#innerIp:,但是现在看来不设置也是没有问题了。

其余在压测脚本上,须要把压测模式选择为内网压测。

参数文件

如果我们先用模板模式写一个大略的带参数的压测脚本,然后切换到脚本模式,可以创造和没有参数化比,紧张改了

1 参数化干系引用

from com.aliyun.pts import DsvReaderfrom com.aliyun.pts import ParamManager

2 __call__(self)里调用params.nextRecord(u'xx.csv')使得参数文件进入下一行

3 用params.getParamValue(u'xx.csv:uid')等对参数进行更换

我们写了个order.csv文件,内容如下:

然后针对前面提到的3处修正点,修正了我们的脚本,这样每次要求都会到参数文件里获取不同的参数来发要求l。
再把参数文件上传上来,末了调试截图:

可以看到

日志打印

为了验证日志打印功能,我们可以在脚本里打印一些日志。
末了算上前面提到的参数等功能,末了的完全脚本为:

#! /usr/bin/env python # -- coding: utf-8 --# PTS Script Version 1.0# PTS脚本SDK:框架API、常用HTTP要求/相应处理APIfrom util import PTSfrom HTTPClient import NVPairfrom HTTPClient import Cookiefrom HTTPClient import HTTPRequestfrom HTTPClient import CookieModulefrom com.aliyun.pts import DsvReaderfrom com.aliyun.pts import ParamManager

大家可以比拟一下这个版本和上个版本的差异,就可以很清楚的知道这两个功能的用法。

压测过程

还是和以前一样的压,并发先创建一个场景,这次设置施压机为3台,这样可以3台3台地增加并发数。
然后从3台开始,再改成30,再到300,末了到600愣住,看到延迟已经超过预期了。
须要把稳的是,一开始是由于相应韶光超过预期才停滞增加并发数的,但网站不能做事是600并发保持了约四分钟后发生的。

先截个压测场景的图,由于是内网压测,把稳施压机所在集群和ECS要一样。

然后看业务指标。
首先TPS基本没变革。
到后来做事运行了一段韶光后挂了,tps就掉到0了。

随着并发的增加,相应韶光增加了。
但是由于后来做事不可用后相应韶光变得太长了导致前面的相应增加看不出来了。

我用其余一次在涌现问题就停手的压测记录截图来看大家会清楚一些,这里的相应韶光的几个颠簸分别是3->30->300->600带来的。

并发是是3->30->300->600

接下来是ECS的负载

然后是RDS的

由于RDS的监控是5分钟1次,我压测的韶光不长截出来图并不好看。
不过可以看到,数据库的负载不高

结果总结

这次是比较范例的把做事器压挂了的例子,我们登录到做事器上,用TOP命令看到CPU已经满了。

把11941这个PID用jstack 11941 > ~/11941.dump抓个现场,打开看下。
里面茫茫多的

虽然从Mysql上不论是监控还是实例诊断报告上看都很正常,但是从这里可以判断是连接数据库写入订单数据的步骤涌现问题。
涌现问题的是通过Buy里利用synchronized申请com.mysql.jdbc.JDBC4Connection工具锁未成功。
到这里再联系前面的数据库连接数一共就2个(个中有1个还是DMS用掉的,实在只用掉了一个),终于徒然大悟:之前做demo的时候图方便,数据库的连接是利用单实例模式做的,全部的要求用的是同一个数据库连接。
转头可以考虑用数据库连接池配一个该当可以提高运用的性能。