最近在学习 gatling,在网上找了找资料,主要是 gatling 的官网和 stackoverflow。中文的资料一般都特别基础和简单。
翻译 gatling 的官方文档是因为自己看过了官网资料,觉得特别适合入门,就分享给大家。本人英语渣渣,翻译得天马行空,请大家多见谅。
更新情况,视个人时间,争取每周发两篇。
任何意见和建议都请留言。
后续可能会做一个 gatling 的测试框架,不过这个就不一定什么时候了……
欢迎转载,不过请注明出处。
原文请移步:http://gatling.io/docs/2.1.7/advanced_tutorial.html#advanced-tutorial
在这一部分,我们假设读者已经完成了前面 Quickstart 的学习部分。而且你已经有了一个基础的 simulation。
我们在这一部分将通过一系列的重构,向读者介绍更多 Gatling 的高级用法和 DSL 结构。
回顾下已有的 Simulation:
package computerdatabase
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class BasicSimulation extends Simulation {
val httpConf = http
.baseURL("http://computer-database.gatling.io")
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.doNotTrackHeader("1")
.acceptLanguageHeader("en-US,en;q=0.5")
.acceptEncodingHeader("gzip, deflate")
.userAgentHeader("Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0")
val scn = scenario("BasicSimulation")
.exec(http("request_1")
.get("/"))
.pause(5)
setUp(
scn.inject(atOnceUsers(1))
).protocols(httpConf)
}
现在,我们的 simulation 脚本,是一大整块的场景。
首先,我们得把它拆分成组合在一起的各个进程,就像 Selenium 里面的 Page Object 模式一样(译者简单解释下 Selenium 的 Page Object Pattern:核心就是实现页面与元素分离,方便扩展和维护)。这样做的好处是,你可以轻松地服用某些部分去完成复杂的操作,而不用牺牲程序的稳定性。
在我们的场景中,我们有三个彼此分开的进程(其实就是要测试的三个场景):
Search:用名字搜索 models;
Browse :以 list 的形式浏览你的 models;
Edit : 编辑特定的 model;
我们会把它们提取出来,放到对象里面。这里的对象,指的是 Scala 的单例;你可以把它们创建到指定的文件里面,或者和 Simulation 放到一起。
object Search {
val search = exec(http("Home") // let's give proper names, as they are displayed in the reports
.get("/"))
.pause(7)
.exec(http("Search")
.get("/computers?f=macbook"))
.pause(2)
.exec(http("Select")
.get("/computers/6"))
.pause(3)
}
object Browse {
val browse = ???
}
object Edit {
val edit = ???
}
我们现在就可以用可重用的事务进程,重写我们的脚本:
val scn = scenario("Scenario Name").exec(Search.search, Browse.browse, Edit.edit)
看看我们的脚本,只有一个虚拟用户(Vuser),你是在逗我吗?
怎么办,加呗!
我们定义两个用户群体:
普通用户:他们只能 search 和 browse models
管理员用户:他们能 search、browse 和 edit models
用脚本实现的场景是这样的:
val users = scenario("Users").exec(Search.search, Browse.browse)
val admins = scenario("Admins").exec(Search.search, Browse.browse, Edit.edit)
下面这段话就是用来设置虚拟用户的,里面的那个 10:
setUp(users.inject(atOnceUsers(10)).protocols(httpConf))
这样,我们就设置了 10 个并发用户。我们就这 10 个用户,因为我们不想一下子就挂掉 server。Server 是人类的好朋友,慈悲点有好处的。
你要是有 3000 个并发用户的话,估计你也不想一下子就全都启动。因为,真实的用户行为也是逐渐地进行访问的(译者:一般的性能测试工具做不到同时起来那么多 Vuser)。
Gatling 提供一种叫做 rampUsers 的虚拟用户可以模拟这种行为。ramp user 就是指,被一点一点排着队启动的虚拟用户。
下面这么写就是设置了 10 个普通用户和 2 个管理用户,他们每人工作 10 秒钟,因为我们要善待服务器:
setUp(
users.inject(rampUsers(10) over (10 seconds)),
admins.inject(rampUsers(2) over (10 seconds))
).protocols(httpConf)
现在我们已经知道了如何创建一大堆不同权限的虚拟用户了。但有个问题,他们都在搜索一个 model 啊,太傻了,得让他们干点不同的事啊!
我们得弄点动态的数据,让我们的 Vuser 实现不同的场景,并以不同的方式结束。这就是我们为什么需要 Feeder。
Feeder 究竟是啥?其实 Feeder 就是你想用的动态数据源。一种最简单常见的 Feeder 就是 CSV。我们下面就看看怎么用 CSV 存储数据:
首先,我们建一个叫做 search.csv 的文件,放在 user-files/data 文件夹下面。
这个文件里面就如下 3 行东西:
searchCriterion,searchComputerName
Macbook,MacBook Pro
eee,ASUS Eee PC 1005PE
现在,我们就用上面的数据做一个动态的 Feeder。
object Search {
val feeder = csv("search.csv").random // 1, 2
val search = exec(http("Home")
.get("/"))
.pause(1)
.feed(feeder) // 3
.exec(http("Search")
.get("/computers?f=${searchCriterion}") // 4
.check(css("a:contains('${searchComputerName}')", "href").saveAs("computerURL"))) // 5
.pause(1)
.exec(http("Select")
.get("${computerURL}")) // 6
.pause(1)
}
解释下:
想了解更多的 Feeder 相关,点这个:http://gatling.io/docs/2.1.7/session/feeder.html#feeder
想了解更多的 Http Check 相关,点这个:http://gatling.io/docs/2.1.7/session/feeder.html#feeder
我们发现,在 browse 这个进程中,我们有大量的操作都是通过页面循环,这样就带来了很多重复。我们使用了不同的请求参数发了 4 次相同的请求。
这就违反了 DRY(别写重复代码)原则,下面我们来优化这点:
首先,我们把重复的 exec 抽出来,封装成一个函数。Simulation 是一个典型的 Scala 类,所以你可以使用任何 Scala 的语法(译者特别讨厌作者无休止地安利 Scala)。
object Browse {
def gotoPage(page: Int) = exec(http("Page " + page)
.get("/computers?p=" + page))
.pause(1)
val browse = exec(gotoPage(0), gotoPage(1), gotoPage(2), gotoPage(3), gotoPage(4))
}
上面的代码通过页面的号码访问,这就减少了代码重复。
但是下面这个写法更臭屁:
object Browse {
val browse = repeat(5, "n") { // 1
exec(http("Page ${n}")
.get("/computers?p=${n}")) // 2
.pause(1)
}
}
解释下:
了解更多循环,请访问:http://gatling.io/docs/2.1.7/general/scenario.html#scenario-loops
目前为止,我们一直在使用 check 来从 html 返回中校验一些数据。但其实 check 是可以检查网络状态码的。
check 的默认值就是检查 20X 和 304.
为了介绍 Gatling 的失败处理机制,我们使用 check 检查一个随机失败的脚本
import java.util.concurrent.ThreadLocalRandom // 1
val edit = exec(http("Form")
.get("/computers/new"))
.pause(1)
.exec(http("Post")
.post("/computers")
.check(status.is(session => 200 + ThreadLocalRandom.current.nextInt(2)))) // 2
解释下:
管理这种随机失败的场景,我们需要使用两个叫做 tryMax 和 exitHereIfFailed 的东东,大致结构如下:
val tryMaxEdit = tryMax(2) { // 1
exec(edit)
}.exitHereIfFailed // 2
解释下:
场景条件相关点击这里:http://gatling.io/docs/2.1.7/general/scenario.html#scenario-conditions
下载官方安装包后,这些代码都会在 user-files/simulations 里面,读者可以看得到。
就这些了,学会了就是 Gatling 高级用户了(译者觉得懂了这些其实才刚刚开始而已)!