Gatling是一款基于Scala 开发的高性能服务器性能测试工具,它主要用于对服务器进行负载等测试,并分析和测量服务器的各种性能指标。目前仅支持http协议,可以用来测试web应用程序和RESTful服务。
除此之外它拥有以下特点:
打开 IDEA ,点击【IntelliJ IDEA】 -> 【Preferences】 -> 【Plugins】,搜索 “Scala”,搜索到插件然后点击底部的 【Install JetBrains plugin…】安装重启即可。
创建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>
工程项目结构如下图:
项目目录说明:
bin\gatling.bat
和bin\gatling.sh
效果一致
bin\recorder.bat
和bin\recorder.sh
效果一致,录制的脚本存放在scala目录下
至此就可以使用IntelliJ愉快的开发啦。
Gatling基于Scala开发的压测工具,我们可以通过录制自动生成脚本,也可以自己编写脚本,大家不用担心,首先脚本很简单常用的没几个,另外gatling封装的也很好我们不需要去专门学习Scala语法,当然如果会的话会更好。
代码如下
<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基于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脚本的编写主要包含下面三个步骤
我们以百度为例,进行第一个GET请求测试脚本的编写,类必须继承 Simulation
//设置请求的根路径
val httpConf = http.baseURL("http://localhost:8080")
val scn = scenario("SpringBootSimulation").during(100){
exec(http("springboot_home").get("/helloworld"))
}
//设置线程数
setUp(scn.inject(atOnceUsers(10)).protocols(httpConf))
这样我们一个简单的脚本就完成了,可以运行看下效果。
部分测试报告如下:
注入方法用来定义虚拟用户的操作
setUp(
scn.inject(
nothingFor(4 seconds), // 1
atOnceUsers(10), // 2
rampUsers(10) over(5 seconds), // 3
constantUsersPerSec(20) during(15 seconds), // 4
constantUsersPerSec(20) during(15 seconds) randomized, // 5
rampUsersPerSec(10) to 20 during(10 minutes), // 6
rampUsersPerSec(10) to 20 during(10 minutes) randomized, // 7
splitUsers(1000) into(rampUsers(10) over(10 seconds)) separatedBy(10 seconds), // 8
splitUsers(1000) into(rampUsers(10) over(10 seconds)) separatedBy atOnceUsers(30), // 9
heavisideUsers(1000) over(20 seconds) // 10
).protocols(httpConf)
)
setUp(scn.inject(atOnceUsers(50)).protocols(httpConf))
setUp(scn.inject(rampUsers(50) over(30 seconds)).protocols(httpConf))
setUp(scn.inject(constantUsersPerSec(30) during(15 seconds)).protocols(httpConf))
setUp(scn.inject(constantUsersPerSec(30) during(15 seconds) randomized).protocols(httpConf))
setUp(scn.inject(rampUsersPerSec(30) to (50) during(15 seconds)).protocols(httpConf))
setUp(scn.inject(rampUsersPerSec(30) to (50) during(15 seconds) randomized).protocols(httpConf))
setUp(scn.inject(heavisideUsers(50) over(15 seconds)).protocols(httpConf))
setUp(scn.inject(splitUsers(50) into(rampUsers(10) over(10 seconds)) separatedBy(10 seconds)).protocols(httpConf))
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(times,counterName)
times:循环次数
counterName:计数器名称,可选参数,可以用来当当前循环下标值使用,从0开始
val scn = scenario("BaiduSimulation").repeat(100){
exec(http("baidu_home").get("/"))
}
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参数提交方式:
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)
}
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))
/resources/bodies
下
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)
}
Gatling对参数的处理称为Feeder[供料器],支持主要有:
val feeder = Array(
Map("foo" -> "foo1", "bar" -> "bar1"),
Map("foo" -> "foo2", "bar" -> "bar2"),
Map("foo" -> "foo3", "bar" -> "bar3"))
val csvFeeder = csv("foo.csv")//文件路径在 %Gatling_Home%/user-files/data/
val jsonFileFeeder = jsonFile("foo.json")
//json的形式:
[
{
"id":19434,
"foo":1
},
{
"id":19435,
"foo":2
}
]
jdbcFeeder("databaseUrl", "username", "password", "SELECT * FROM users")
feed()
在每次执行时都会从Iterator[Map[String, T]]
对象中取出一个值,这样才能实现动态参数的需求。
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))
}