前言

Gatling是一款基于Scala 开发的高性能服务器性能测试工具,它主要用于对服务器进行负载等测试,并分析和测量服务器的各种性能指标。目前仅支持http协议,可以用来测试web应用程序和RESTful服务。

除此之外它拥有以下特点:

  • 支持Akka Actors 和 Async IO,从而能达到很高的性能

  • 支持实时生成Html动态轻量报表,从而使报表更易阅读和进行数据分析

  • 支持DSL脚本,从而使测试脚本更易开发与维护

  • 支持录制并生成测试脚本,从而可以方便的生成测试脚本

  • 支持导入HAR(Http Archive)并生成测试脚本

  • 支持Maven,Eclipse,IntelliJ等,以便于开发

  • 支持Jenkins,以便于进行持续集成

  • 支持插件,从而可以扩展其功能,比如可以扩展对其他协议的支持

  • 开源免费

依赖工具

  • Maven

  • JDK

  • Intellij IDEA

安装Scala插件

打开 IDEA ,点击【IntelliJ IDEA】 -> 【Preferences】 -> 【Plugins】,搜索 “Scala”,搜索到插件然后点击底部的 【Install JetBrains plugin…】安装重启即可。

Gatling Maven工程

创建Gatling提供的gatling-highcharts-maven-archetype,

在 IntelliJ中选择 New Project -> Maven -> Create form archetype -> Add Archetype,在弹出框中输入一下内容:

 GroupId: io.gatling.highcharts
ArtifactId: gatling-highcharts-maven-archetype
Version: 3.0.0-RC3

点击查看最新版本: 最新版本

之后输入你项目的GroupId(包名)和ArtifactId(项目名)来完成项目创建,

项目创建完成后,Maven会自动配置项目结构。

注:在创建的工程,修改pom.xml文件,添加如下配置,加快构建速度:

 <repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>

工程项目目录

工程项目结构如下图:

项目目录说明:

  • bodies:用来存放请求的body数据

  • data:存放需要输入的数据

  • scala:存放Simulation脚本

  • Engine:右键运行跟运行 bin\gatling.batbin\gatling.sh效果一致

  • Recorder:右键运行跟运行 bin\recorder.batbin\recorder.sh效果一致,录制的脚本存放在scala目录下

  • target:存放运行后的报告

至此就可以使用IntelliJ愉快的开发啦。

Gatling测试SpringBoot

Gatling基于Scala开发的压测工具,我们可以通过录制自动生成脚本,也可以自己编写脚本,大家不用担心,首先脚本很简单常用的没几个,另外gatling封装的也很好我们不需要去专门学习Scala语法,当然如果会的话会更好。

SpringBoot测试工程示例

Maven依赖

代码如下

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

控制层接口

代码如下:

@RestController
public class HelloWorldController {
@RequestMapping("/helloworld")
public String sayHelloWorld(){
return "hello World !";
}
}

浏览器演示

Gatling测试脚本编写

Gatling基于Scala开发的压测工具,我们可以通过录制自动生成脚本,也可以自己编写脚本,大家不用担心,首先脚本很简单常用的没几个,另外gatling封装的也很好我们不需要去专门学习Scala语法,当然如果会的话会更好。

脚本示例

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

class SpringBootSimulation extends Simulation{
//设置请求的根路径
val httpConf = http.baseUrl("http://localhost:8080")
/*
运行100秒 during 默认单位秒,如果要用微秒 during(100 millisecond)
*/
val scn = scenario("SpringBootSimulation").during(100){
exec(http("springboot_home").get("/helloworld"))
}
//设置线程数
// setUp(scn.inject(rampUsers(500) over(10 seconds)).protocols(httpConf))
setUp(scn.inject(atOnceUsers(10)).protocols(httpConf))
}

脚本编写

Gatling脚本的编写主要包含下面三个步骤

  • http head配置

  • Scenario 执行细节

  • setUp 组装

我们以百度为例,进行第一个GET请求测试脚本的编写,类必须继承 Simulation

  1. 配置下head,只是简单的请求下百度首页,所以只定义下请求的base url,采用默认的http配置即可

    //设置请求的根路径
    val httpConf = http.baseURL("http://localhost:8080")
  2. 声明Scenario,指定我们的请求动作

    val scn = scenario("SpringBootSimulation").during(100){
    exec(http("springboot_home").get("/helloworld"))
    }

    scenario里的参数:scenario name   exec()里的参数就是我们的执行动作,http(“本次请求的名称”).get(“本次http get请求的地址”)

  3. 设置并发数并组装

     //设置线程数
    setUp(scn.inject(atOnceUsers(10)).protocols(httpConf))

    atOnceUsers:立马启动的用户数,可以理解为并发数

这样我们一个简单的脚本就完成了,可以运行看下效果。

部分测试报告如下:

高级教程

Injection – 注入

注入方法用来定义虚拟用户的操作

 setUp(
scn.inject(
nothingFor(4 seconds), //
atOnceUsers(10), //
rampUsers(10) over(5 seconds), //
constantUsersPerSec(20) during(15 seconds), //
constantUsersPerSec(20) during(15 seconds) randomized, //
rampUsersPerSec(10) to 20 during(10 minutes), //
rampUsersPerSec(10) to 20 during(10 minutes) randomized, //
splitUsers(1000) into(rampUsers(10) over(10 seconds)) separatedBy(10 seconds), //
splitUsers(1000) into(rampUsers(10) over(10 seconds)) separatedBy atOnceUsers(30), //
heavisideUsers(1000) over(20 seconds) //
).protocols(httpConf)
)
  1. nothingFor(duration):设置一段停止的时间

  2. atOnceUsers(nbUsers):立即注入一定数量的虚拟用户

    setUp(scn.inject(atOnceUsers(50)).protocols(httpConf))
  3. rampUsers(nbUsers) over(duration):在指定时间内,设置一定数量逐步注入的虚拟用户

    setUp(scn.inject(rampUsers(50) over(30 seconds)).protocols(httpConf))
  4. constantUsersPerSec(rate) during(duration):定义一个在每秒钟恒定的并发用户数,持续指定的时间

     setUp(scn.inject(constantUsersPerSec(30) during(15 seconds)).protocols(httpConf))
  5. constantUsersPerSec(rate) during(duration) randomized:定义一个在每秒钟围绕指定并发数随机增减的并发,持续指定时间

     setUp(scn.inject(constantUsersPerSec(30) during(15 seconds) randomized).protocols(httpConf))
  6. rampUsersPerSec(rate1) to (rate2) during(duration):定义一个并发数区间,运行指定时间,并发增长的周期是一个规律的值

     setUp(scn.inject(rampUsersPerSec(30) to (50) during(15 seconds)).protocols(httpConf))
  7. rampUsersPerSec(rate1) to(rate2) during(duration) randomized:定义一个并发数区间,运行指定时间,并发增长的周期是一个随机的值

    setUp(scn.inject(rampUsersPerSec(30) to (50) during(15 seconds) randomized).protocols(httpConf))
  8. heavisideUsers(nbUsers) over(duration):定义一个持续的并发,围绕和海维赛德函数平滑逼近的增长量,持续指定时间(译者解释下海维赛德函数,H(x)当x>0时返回1,x<0时返回0,x=0时返回0.5。实际操作时,并发数是一个成平滑抛物线形的曲线)

    setUp(scn.inject(heavisideUsers(50) over(15 seconds)).protocols(httpConf))
  9. splitUsers(nbUsers) into(injectionStep) separatedBy(duration):定义一个周期,执行injectionStep里面的注入,将nbUsers的请求平均分配

    setUp(scn.inject(splitUsers(50) into(rampUsers(10) over(10 seconds)) separatedBy(10 seconds)).protocols(httpConf))
  10. splitUsers(nbUsers) into(injectionStep1) separatedBy(injectionStep2):使用injectionStep2的注入作为周期,分隔injectionStep1的注入,直到用户数达到nbUsers

    setUp(scn.inject(splitUsers(100) into(rampUsers(10) over(10 seconds)) separatedBy atOnceUsers(30)).protocols(httpConf))

循环

val scn = scenario("BaiduSimulation").
exec(http("baidu_home").get("/"))

上面的测试代码运行时只能跑一次,为了测试效果,我们需要让它持续运行一定次数或者一段时间,可以使用下面两个方式:

  • repeat

      repeat(times,counterName)
    times:循环次数
    counterName:计数器名称,可选参数,可以用来当当前循环下标值使用,从0开始
 val scn = scenario("BaiduSimulation").repeat(100){
exec(http("baidu_home").get("/"))
}
  • during

    during(duration, counterName, exitASAP)
    duration:时长,默认单位秒,可以加单位milliseconds,表示毫秒
    counterName:计数器名称,可选。很少使用
    exitASAP:默认为true,简单的可以认为当这个为false的时候循环直接跳出,可在
    循环中进行控制是否继续
  /*
运行100秒 during 默认单位秒,如果要用微秒 during(100 millisecond)
*/
val scn = scenario("BaiduSimulation").during(100){
exec(http("baidu_home").get("/"))
}

POST请求

post参数提交方式:

  • JSON方式

     import io.gatling.core.Predef._
    import io.gatling.core.scenario.Simulation
    import io.gatling.http.Predef._
    class JsonSimulation extends Simulation {
    val httpConf = http.baseURL("http://127.0.0.1:7001/tst")
    //注意这里,设置提交内容type
    val headers_json = Map("Content-Type" -> "application/json")
    val scn = scenario("json scenario")
    .exec(http("test_json") //http 请求name
    .post("/order/get") //post url
    .headers(headers_json) //设置body数据格式
    //将json参数用StringBody包起,并作为参数传递给function body()
    .body(StringBody("{\"orderNo\":201519828113}")))
    setUp(scn.inject(atOnceUsers(10))).protocols(httpConf)
    }
     
  • Form方式

    import io.gatling.core.Predef._
    import io.gatling.http.Predef._
    class FormSimulation extends Simulation {
    val httpConf = http
    .baseURL("http://computer-database.gatling.io")
    //注意这里,设置提交内容type
    val contentType = Map("Content-Type" -> "application/x-www-form-urlencoded")
    //声明scenario
    val scn = scenario("form Scenario")
    .exec(http("form_test") //http 请求name
    .post("/computers") //post地址, 真正发起的地址会拼上上面的baseUrl http://computer-database.gatling.io/computers
    .headers(contentType)
    .formParam("name", "Beautiful Computer") //form 表单的property name = name, value=Beautiful Computer
    .formParam("introduced", "2012-05-30")
    .formParam("discontinued", "")
    .formParam("company", "37"))
    setUp(scn.inject(atOnceUsers(1)).protocols(httpConf))
  • RawFileBody

      import io.gatling.core.Predef._
    import io.gatling.core.scenario.Simulation
    import io.gatling.http.Predef._
    class JsonSimulation extends Simulation {
    val httpConf = http.baseURL("http://127.0.0.1:7001/tst")
    //注意这里,设置提交内容type
    val headers_json = Map("Content-Type" -> "application/json")
    val scn = scenario("json scenario")
    .exec(http("test_json") //http 请求name
    .post("/order/get") //post url
    .headers(headers_json) //设置body数据格式
    //将json参数用StringBody包起,并作为参数传递给function body()
    .body(RawFileBody("request.txt"))
    setUp(scn.inject(atOnceUsers(10))).protocols(httpConf)
    }

    txt的文件内容为JSON数据,存放目录/resources/bodies

Feed 动态参数

Gatling对参数的处理称为Feeder[供料器],支持主要有:

  • 数组

     val feeder = Array(
    Map("foo" -> "foo1", "bar" -> "bar1"),
    Map("foo" -> "foo2", "bar" -> "bar2"),
    Map("foo" -> "foo3", "bar" -> "bar3"))
  • CSV文件

    val csvFeeder = csv("foo.csv")//文件路径在 %Gatling_Home%/user-files/data/
  • JSON文件

     val jsonFileFeeder = jsonFile("foo.json")
    //json的形式:
    [
    {
    "id":19434,
    "foo":1
    },
    {
    "id":19435,
    "foo":2
    }
    ]
  • JDBC数据

    jdbcFeeder("databaseUrl", "username", "password", "SELECT * FROM users")
  • Redis

    可参看官方文档http://gatling.io/docs/2.1.7/session/feeder.html#feeder
    

    使用示例:

    import io.gatling.core.Predef._
    import io.gatling.core.scenario.Simulation
    import io.gatling.http.Predef._
    import scala.concurrent.duration._
    /**
    * region请求接口测试
    */
    class DynamicTest extends Simulation {
    val httpConf = http.baseURL("http://127.0.0.1:7001/test")
    //地区 feeder
    val regionFeeder = csv("region.csv").random
    //数组形式
    val mapTypeFeeder = Array(
    Map("type" -> ""),
    Map("type" -> "id_to_name"),
    Map("type" -> "name_to_id")).random
    //设置请求地址
    val regionRequest =
    exec(http("region_map").get("/region/map/get"))
    //加载mapType feeder
    .feed(mapTypeFeeder)
    //执行请求, feeder里key=type, 在下面可以直接使用${type}
    .exec(http("province_map").get("/region/provinces?mType=${type}"))
    //加载地区 feeder
    .feed(regionFeeder)
    //region.csv里title含有provinceId和cityId,所以请求中直接引用${cityId}/${provinceId}
    .exec(http("county_map").get("/region/countties/map?mType=${type}&cityId=${cityId}&provinceId=${provinceId}"))
    //声明scenario name=dynamic_test
    val scn = scenario("dynamic_test")
    .exec(during(180){ regionRequest
    })
    //在2秒内平滑启动150个线程(具体多少秒启动多少线程大家自己评估哈,我这里瞎写的)
    setUp(scn.inject(rampUsers(150) over (2 seconds)).protocols(httpConf))
    }

    注意:通过下面的代码只会第一次调用生成一个随机数,后面调用不变

    exec(http("Random id browse")
    .get("/articles/" + scala.util.Random.nextInt(100))
    .check(status.is(200))

    Gatling的官方文档解释是,由于DSL会预编译,在整个执行过程中是静态的。因此Random在运行过程中就已经静态化了,不会再执行。应改为Feeder实现,Feeder是gatling用于实现注入动态参数或变量的,改用Feeder实现:

    val randomIdFeeder =
    Iterator.continually(Map("id" ->
    (scala.util.Random.nextInt(100)))) feed(randomIdFeeder)
    .exec(http("Random id browse")
    .get("/articles/${id}"))
    .check(status.is(200))

    feed()在每次执行时都会从Iterator[Map[String, T]]对象中取出一个值,这样才能实现动态参数的需求。

Gatling简单测试SpringBoot工程的更多相关文章

  1. 构建简单的Maven工程,使用测试驱动的方式开发项目

    构建简单的Maven工程很简单,这里写这篇随笔的原因是希望自己能记住几个小点. 一.安装Maven 1.下载maven:https://maven.apache.org/download.cgi 2. ...

  2. springboot+nginx+https+linux实现负载均衡加域名访问简单测试

    把springboot项目打包成三个jar包,并指定端口为 14341,14342,14343 下载腾讯云免费ssl证书,解压后会出现如下图文件夹 把nginx文件夹下的 .crt 和 .key文件复 ...

  3. idea 创建springboot工程

    公司最近用springboot做微服务开发 1,使用idea创建一个spring initializr 工程 2,点击next 3,配置好后继续next 4,可以勾选上web,继续next 5,fin ...

  4. Spring Boot2 系列教程 (二) | 第一个 SpringBoot 工程详解

    微信公众号:一个优秀的废人 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 哎呦喂,按照以往的惯例今天周六我的安排应该是待在家学学猫叫啥的.但是今年这种日子就可能一去不复返了,没法办法啊.前 ...

  5. 使用Maven新建SpringBoot工程

    最近用IDEA插件创建Springboot项目,总是403,估计被墙了! 那么这里在提供两种方法 1.从官网下载模板,导入IDEA内 2.使用Maven创建 方法一:打开 https://start. ...

  6. 快速搭建springboot工程

    一.创建SpringBoot的三种方式 在线创建: https://start.spring.io 在IntelliJ IDEA中选择Spring Initializr创建:或者在Eclipse中选择 ...

  7. IntelliJ IDEA 15 部署Tomcat及创建一个简单的Web工程

    一.部署Tomcat 二.创建一个简单的Web工程 2.1创建一个新工程 创建一个新工程 设置JDK及选择Web Application (创建的是Web工程) 点击Next,选择工作空间,起个工程名 ...

  8. struts2+hibernate+spring注解版框架搭建以及简单测试(方便脑补)

    为了之后学习的日子里加深对框架的理解和使用,这里将搭建步奏简单写一下,目的主要是方便以后自己回来脑补: 1:File--->New--->Other--->Maven--->M ...

  9. struts2+hibernate+spring配置版框架搭建以及简单测试(方便脑补)

    为了之后学习的日子里加深对框架的理解和使用,这里将搭建步奏简单写一下,目的主要是方便以后自己回来脑补: 1:File--->New--->Other--->Maven--->M ...

随机推荐

  1. Node六-模块化

    Node实现CommonJS规范 -因此node可以使用模块化的方式组织代码结构 简单命令行加减运算 -代码 命令行执行 V8对es6的支持 -直接支持:const.模版字符串等. -严格模式支持:l ...

  2. ZeroMQ 教程 002 : 高级技巧

    本文主要译自 zguide - chapter two. 但并不是照本翻译. 上一章我们简单的介绍了一个ZMQ, 并给出了三个套路的例子: 请求-回应, 订阅-发布, 流水线(分治). 这一章, 我们 ...

  3. 登录测试用例sql语句注入

    利用SQL注入漏洞登录后台的实现方法 作者: 字体:[增加 减小] 类型:转载 时间:2012-01-12我要评论 工作需要,得好好补习下关于WEB安全方面的相关知识,故撰此文,权当总结,别无它意.读 ...

  4. Python微信公众号开发

    最近老大叫我学习开发微信,试着玩了下.网上查了下文档.有点过时. 简单步骤: 1)申请服务器并完成环境配置 去腾讯云购买云服务器.当然你也可以购买其他产品,比如阿里云.因为我是学生,有优惠110一年. ...

  5. oracle批量插入测试数据

    做数据库开发或管理的人经常要创建大量的测试数据,动不动就需要上万条,如果一条一条的录入,那会浪费大量的时间,本文介绍了Oracle中如何通过一条 SQL快速生成大量的测试数据的方法.产生测试数据的SQ ...

  6. 【转】maven跳过单元测试-maven.test.skip和skipTests的区别

    -DskipTests,不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下. -Dmaven.test.skip=true,不执行测试用例,也不编译测试 ...

  7. jQuery-01:on live bind delegate

    摘自:https://www.cnblogs.com/moonreplace/archive/2012/10/09/2717136.html moonreplace这位大牛的 当我们试图绑定一些事件到 ...

  8. 深夜学算法之SkipList:让链表飞

    1. 前言 上次写Python操作LevelDB时提到过,有机会要实现下SkipList.摘录下wiki介绍: 跳跃列表是一种随机化数据结构,基于并联的链表,其效率可比拟二叉查找树. 我们知道对于有序 ...

  9. 000webhost虚拟主机绑定自定义二级域名

    作者:荒原之梦 原文链接:http://zhaokaifeng.com/?p=558 前言: 最近想给导航狗IT信息导航做一个文件服务器专门存放文件,以提供引用或下载.于是,我在000webhost上 ...

  10. GMT与Etc/GMT地区信息的时区转换

    GMT 地区信息的时区 在将来的版本中可能不再支持以下左面一列中的地区信息的时区.可能从 /usr/share/lib/zoneinfo 删除这些文件.左列中的地区信息的时区用右列中对等的时区来替换. ...