HTTP负载测试的目的是测试站点或服务是否能够能在一定的时延范围内每秒处理上万到百万个请求。在Java的世界里,最常用的免费负载测试工具是Apache JMeter。这里介绍另外一个工作——gatling,以及如何将其与一种随机数据生成器一起使用。
Gatling是一个开源的,基于Scala, Akka和Netty的负载测试框架。它首要的特性就是高效。利用有限的资源,Gatling可以生成海量的并发请求。其次,编写测试脚本十分简单和直观。它是基于scala的领域特定语言(DSL)。一个简单的例子如下:
cenario("Standard User")
.exec( http("Access Github") get("http://github.com") )
.pause(2, 3)
.exec( http("Search for 'gatling'") get("http://github.com/search") queryParam("q","gatling") )
.pause(2)
最后,测试生成的报告是一系列包含表格和图表的网页,直观展现测试内容。
单单使用Gatling并不能满足所有负载测试的需求。很多情况下,我们需要使用大量数据进行负载测试。虽然Gatling支持从CSV文件或者SQL数据库读取数据,但是它们会贮存在内存里,并不适用于海量数据。更多的时候,我们为每个请求希望随机生成数据。这里随机数据生成器就派上用场了。log-synth是个简单易用的日志生成器。同时,也可以直接将它作为Java库调用。用户可以使用JSON描述需要生成什么样的数据, 例如
{"name":"foo1", "class":"event", "rate": "0.1/d"},
{"name":"foo2", "class":"event", "start": "2014-01-01", "format":"yyyy-MM-dd HH:mm:ss", "rate": "10/s"},
{"name":"foo3", "class":"event", "format": "MM/dd/yyyy HH:mm:ss", "start": "02/01/2014 00:00:00", "rate": "0.5/s"}
将log-synth和gatling集成不是难事。Gatling是Scala编写的,可以直接调用Java方法。总结起来,集成包含了一下几个步骤
首先,将log-synth相关库文件加入Gatling库(需要处理一些冲突)
而后,定义数据模式,例如 "schema.json"
[
{"name":"id", "class":"id"},
{"name":"name", "class":"name", "type":"first_last"},
{"name":"gender", "class":"string", "dist":{"MALE":0.5, "FEMALE":0.5, "OTHER":0.02}},
{"name":"address", "class":"address"},
{"name":"first_visit", "class":"date", "format":"MM/dd/yyyy"}
]
接下来,编写Gatling脚本时定义data feed
// custom feed generate the usernames randomly.
val myCustomFeeder = new Feeder[String] {
// always return true as this feeder can be polled infinitively
override def hasNext = true
val s: SchemaSampler =
new SchemaSampler(Resources.asCharSource(Resources.getResource("schema.json"), Charsets.UTF_8).read)
override def next: Map[String, String] = {
val record: JsonNode = s.sample
var name = record.get("name").asText
var id = record.get("id").asText
val today = Calendar.getInstance().getTime()
val formater = new SimpleDateFormat("yyyyMMddHHmmssSSS")
var todayStr = formater.format(today)
Map("name" -> name, "id" -> id, "time" -> todayStr)
}
}
最后,使用data feed
val scn = scenario("Write Test")
.feed(myCustomFeeder)
.exec(
http("insert_request")
.put("/keyvalue/test_collection/${id}") // must be lowercase
.header("Content-Type", "application/json")
.body("""{ "username": "${name}" }""").asJSON
.check(status.in(200 to 210))
)
.exec(
http("get_request")
.get("/keyvalue/test_collection/${id}") // must be lowercase
.header("Content-Type", "application/json")
.check(status.in(200 to 210))
)
setUp(
scn.users(threads).ramp(rampup).delay(1).protocolConfig(httpConf)
)
完整的例子在我的github项目gratling-random中。
https://github.com/tongqqiu/gatling-random