一篇入门 — Gatling 性能测试手册

介绍

本篇博客,旨在记录学习的要点,所以格式随意, 方便本人日后自考和回忆,有兴趣的朋友可以评论讨论。
原文地址:https://www.cnblogs.com/clockq/p/10539974.html

一. 性能测试基础

1.1 性能测试时什么?

性能测试时通过自动化的测试工具模拟多种正常峰值、以及异常负载条件,以此来对系统的各项性能指标进行评测。

性能测试 = 负载测试 + 压力测试

通过负载测试,确定在各种工作负载下系统的性能,目的是测试系统的负载逐渐增加的情况下,系统的各项性能指标的变化情况。
通过压力测试,确定一个系统的瓶颈或者不能接受的性能点,来获得系统所能提供的最大服务级别。

1.2 性能测试的目的

评估系统的能力
识别体系中的弱点
系统调优
检查软件中的问题
验证系统稳定性
验证系统可靠性

1.3 性能测试的常见观察指标

Avg Rps: 平均每秒响应次数 = 总请求时间 / 秒数
Avg time to last byte per terstion(mstes): 平均每秒业务脚本迭代次数
Successful Rounds: 成功的请求
Failed Hits: 失败的单击次数
Hits Per Second: 每秒单击次数
Successful Hits Per Second: 每秒成功的单击次数
Failed Hist Per Second: 每秒失败的单击次数
Attempted Connections: 尝试连接数
Throughput: 吞吐率

同时,对于服务端的CPU占有率,内存占有率,数据库连接池等也是需要观察的重点。

1.4 性能测试的基本流程

明确性能测试需求
制定性能测试方案
编写性能测试案例
执行性能测试案例
分析性能测试结果
生成性能测试报告

二. Gatling基础 -> 基础使用法

2.1 安装Gatling

获取安装包 http://gatling.io.download/
下载成功后解压即可 使用Gatling需要安装JDK

2.2 使用Gatling

编写测试脚本(这块重点学习和讲解)或者使用自带的录制器(bin/recorder.sh)
执行测试脚本(bin/gatling.sh),在开启的窗口中选择要执行的脚本
查看测试报告(报告默认在“result/”目录下)
分析测试结果

三. Gatling 和 Mvn 整合使用 (推荐)

3.1 导入依赖

    <properties>
        <gatling.version>2.1.7</gatling.version>
        <gatling-plugin.version>2.1.7</gatling-plugin.version>
    </properties>
    
    <!-- Gatling Module -->
    <dependency>
        <groupId>io.gatling.highcharts</groupId>
        <artifactId>gatling-charts-highcharts</artifactId>
        <version>${gatling.version}</version>
    </dependency>

3.2 导入插件

<build>
    <sourceDirectory>src/test/scala</sourceDirectory>
    <testSourceDirectory>src/test/scala</testSourceDirectory>
    <plugins>
        <!-- Gatling Maven plugin that runs the load-simulation. -->
        <plugin>
            <groupId>io.gatling</groupId>
            <artifactId>gatling-maven-plugin</artifactId>
            <version>${gatling-plugin.version}</version>
            <configuration>
                <configFolder>src/test/resources</configFolder>
                <dataFolder>src/test/resources/data</dataFolder>
                <resultsFolder>target/gatling/results</resultsFolder>
                <runMultipleSimulations>true</runMultipleSimulations>
                <simulationsFolder>src/test/scala/com/pharbers/gatling</simulationsFolder>

                <simulationClass>com.pharbers.gatling.scenario.getHome</simulationClass>

                <!--    <noReports>false</noReports> -->
                <!--   <reportsOnly>directoryName</reportsOnly> -->
                <!--   <simulationClass>foo.Bar</simulationClass> -->
                <!--   <jvmArgs> -->
                <!--     <jvmArg>-DmyExtraParam=foo</jvmArg> -->
                <!--   </jvmArgs> -->
                <!--    <fork>true</fork> -->
                <!--    <propagateSystemProperties>true</propagateSystemProperties> -->
                <!--   <failOnError>true</failOnError> -->
            </configuration>
        </plugin>
    </plugins>
</build>

3.3 编写脚本

忽略
注意: 脚本要写在 src/test/scala 下

3.4 执行脚本

mvn gatling:execute

3.5 分析报告

四. 现实测试举例

我们先以测试“博客园系统登录页”性能为例,讲解一次测试过程的几个步骤,和测试报告怎么分析。

4.1 明确性能测试需求

好的开始是成功的一半

明确性能测试的需求是至关重要的,所以我们要先有一份测试需求实例

测试需求名称: 博客园登录接口性能测试

信息描述 描述内容
参与者 张三
概述 测试博客园登录接口的最大并发量
前置条件 博客园前端页面已经成功部署,并可以正常访问
后置条件
业务数据 测试登录账号
不可测试原因 网络不可达
流程规则 用户访问博客园登录页,滞留5s,之后调用登录接口
业务规则
页面规则
特殊规则
接口规则
检查内容 检查当用户量达到多大时,会导致服务端阻塞,用户响应时间超过5s

4.2 编写性能测试案例

测试需求名称: 博客园登录接口性能测试

测试步骤 步骤描述 预期结果
步骤 1 是否测试博客园登录接口最大并发量 确定性能测试登录接口的并发用户数量
步骤 2 启动博客园的前端工程 前端工程启动成功
步骤 3 准备性能测试脚本 性能测试脚本准备完成
步骤 4 准备测试数据
步骤 5 执行脚本,验证系统是否满足相关性能测试指标 平均响应时长<2s 95%响应时长<= 5s 系统满足相关性能测试指标
步骤 5 执行1小时压力测试 1. 系统满足相关性能测试指标 2. 1小时压力测试中脚本未报错

4.3 执行性能测试案例

按照性能测试案例编写测试脚本

package com.pharbers.gatling.base

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.http.config.HttpProtocolBuilder

object phHttpProtocol {
    implicit val noneWhiteList: io.gatling.core.filter.WhiteList = WhiteList()
    implicit val noneBlackList: io.gatling.core.filter.BlackList = BlackList()
    implicit val staticBlackList: io.gatling.core.filter.BlackList = BlackList(""".*.js""", """.*.css""", """.*.gif""", """.*.jpeg""", """.*.jpg""", """.*.ico""", """.*.woff""", """.*.(t|o)tf""", """.*.png""")
    implicit val staticWhiteList: io.gatling.core.filter.WhiteList = WhiteList(""".*.js""", """.*.css""", """.*.gif""", """.*.jpeg""", """.*.jpg""", """.*.ico""", """.*.woff""", """.*.(t|o)tf""", """.*.png""")

    def apply(host: String)
             (implicit blackLst: io.gatling.core.filter.BlackList, whiteLst: io.gatling.core.filter.WhiteList): HttpProtocolBuilder = { http
                .baseURL(host)
                .inferHtmlResources(blackLst, whiteLst)
                .acceptHeader("application/json, text/javascript, */*; q=0.01")
                .acceptEncodingHeader("gzip, deflate")
                .acceptLanguageHeader("zh-CN,zh;q=0.9,zh-TW;q=0.8")
                .doNotTrackHeader("1")
                .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36")
    }
}

package com.pharbers.gatling.base

object phHeaders {

    val headers_base = Map(
        "Accept" -> "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Upgrade-Insecure-Requests" -> "1")
}
package com.pharbers.gatling.scenario

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.core.structure.ChainBuilder

import com.pharbers.gatling.base.phHeaders.headers_base

object getHome {
	val getHome: ChainBuilder = exec(http("home")
			.get("/")
			.headers(headers_base))
}
package com.pharbers.gatling.scenario

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.core.structure.ChainBuilder

import com.pharbers.gatling.base.phHeaders.headers_json

object userLogin {
	val feeder = csv("loginUser.csv").random
	println(feeder)

	val login: ChainBuilder = exec(http("login")
			.get("/api/user/login")
			.headers(headers_json)
			.body(StringBody("""{ "condition" :  { "email" : "nhwa", "password" : "nhwa" } }""")).asJSON)
}
package com.pharbers.gatling.simulation

import io.gatling.core.Predef._
import scala.concurrent.duration._

import com.pharbers.gatling.scenario._
import com.pharbers.gatling.base.phHttpProtocol

class userLogin extends Simulation {
	import com.pharbers.gatling.base.phHttpProtocol.{noneBlackList, noneWhiteList}

	val httpProtocol = phHttpProtocol("http://192.168.100.141:9000")

	val scn = scenario("user_login")
		.exec(
			getHome.getHome
					.pause(5 seconds),
			userLogin.login
					.pause(60 seconds)
		)

	setUp(scn.inject(rampUsers(1000) over (3 seconds))).protocols(httpProtocol)

}

并执行上述脚本

4.4 分析性能测试结果

看下图,可以看到67% + 8%的请求可以在1.2s内完全,同时在1000用户的并发测试下,会有用户请求不到资源,也就是加载失败。

其实,这个地方,可以通过修改gatling.conf来改变表格的渲染区间,使结果更符合我们的测试要求

image

这里,75th的总响应时间=1s,还是很快的,但95th的总响应时间>9s, 所以不符合我们的测试要求。
image

我们使用递增的方式,在3s内逐渐增加用户并发量,并且用户会滞留5s + 60s,在下图中就得到了体现
image

下图是本次测试,在每个时间点的请求情况,包含请求状态(成功,失败)和请求数量
image

还有更多图表,就不一一展示了,我们主要就是查看前两个图表,以此判断服务器所能承受的压力。

当然,如果需要考查更多标准,就需要查看其它图表,比如延迟分布图,负载分布图等等。。。。

4.5 生成性能测试报告

一份合格的性能测试报告,至少应该包含如下内容:

测试基本信息: 包含: 测试目的,报告目标读者,术语定义,参考资料
测试环境描述: 包含: 服务器软硬件环境,网络环境,测试工具,测试人员
性能测试案例执行分析: 需要详细描述每个测试案例的执行情况,以及对对应测试结果进行分析
测试结果综合分析及建议:对本次性能测试做综合分析,并给出测试结论和改进建议
测试经验总结

博客园登录接口性能测试报告

测试信息

信息描述 描述内容
测试人员 齐钟昱
测试目的 检查当用户量达到多大时,会导致服务端阻塞,用户响应时间超过5s
术语定义 50th,安装递增排序后,排在50%的请求的信息
术语定义 95th,安装递增排序后,排在95%的请求的信息
参考资料 零成本实现Web性能测试[电子工业出版社]

测试环境

信息描述 描述内容
服务器系统 CentOS Linux release 7.4.1708 (Core)
服务器集群数量 4
服务器内存(台) 16G
服务器CPU核心数(台) 12
服务器硬盘空间(台) 256G SSD
JAVA版本 1.8.121
Scala版本 2.11.8
Play版本 2.5.0-M2
Redis版本 4.0.1
MongoDB版本 3.4.4
Node.js 8.11.2
Ember.js 2.18.2
网络环境 公司局域网
测试工具 Gatling 2.1.7

结果分析

测试内容 预期结果 测试结果 备注
博客园系统登录页的最大访问量 在当前环境下可以1000用户并发,不会造成用户请求失败 在3s内逐渐提高并发量,当并发量在643时有三个资源请求失败,在并发量达到689时,有64个资源请求失败 未通过,当前博客园系统登录页的最大访问量应小于643
博客园系统登录接口的最大并发量 在当前环境下可以1000用户并发,不会造成用户请求失败 在3s内逐渐提高并发量,当并发量达到1000时,请求资源仍全部成功 通过
博客园登录页的响应时间 在当前环境下用户平均响应时长<2s 95%响应时长<= 5s 50th响应时间为1.6s,95th为22s 未通过
博客园登录接口的响应时间 在当前环境下用户平均响应时长<2s 95%响应时长<= 5s 50th响应时间 < 1s,95th < 1s 通过

测试总结

根据上述分析报告,本次性能测试为通过制定要求,博客园系统登录功能的最大并发量应小于643,为保持性能,建议并发数小于500

五. 常用脚本api

5.1 并发量控制

atOnceUsers(100) 使用100并发量测试目标服务器
rampUsers(100) over (10 seconds) 循序渐进的增大压力,在10s中内线性增加用户数达到最大压力100并发量
nothingFor(10 seconds) 等待10s
constantUsersPerSec(rate) during(duration) 在指定duration内,以固定频率注入用户,每秒注入rate个用户,默认固定间隔
constantUsersPerSec(rate) during(duration) randomized 与上面不同的是用户以随机间隔注入
rampUsersPerSec(rate1) to (rate2) during(duration) 在指定duration内,以递增频率注入用户,每秒注入 rate1 ~ rate2 个用户

5.2 用户行为控制

.exec() 实际的用户行为
.pause(20) 用户滞留20s,模拟用户思考或者浏览内容
.pause(min: Duration, max: Duration) 用户随机滞留,滞留时间在min ~ max 之间

5.3 流程控制

repeat(time, counterName) 内置循环器
foreach(seq, elem, counterName) foreach循环器
csv("file").random 创建填充器
doIf("", "") 判断语句

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注