现在在工作中开始搞 Go 了,重构了一个工具,查了一波如何做性能测试,就当记个笔记。
首先搜索了一下如何给 HTTP server 做压力测试,看了看 wrk 。
wrk 是一个简单的 HTTP server 性能测试工具,使用方式比较简单,只需要安装后命令行跑一跑就可以对 HTTP server 进行压力测试。
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
这条命令是跑一个 benchmark,持续 30s,用 12 个 thread,保持 400 个 HTTP 连接。
不仅如此,wrk 还支持写 lua 脚本自定义请求。
-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
我当时需要使用的 method 是 PUT,直接无脑换上也是可以使用,写起来很简单。
使用了一下,感觉作为一个普通的性能测试工具是可以的,但是不符合我的使用场景,我所做的工具是开一个 HTTP server,一个 websocket server,HTTP server 负责接收数据,websocket server 负责把接收到的发给订阅的 client。所以仅凭 HTTP server 能扛得住请求是不够的,需要验证到底消息有没有走通。所以 wrk 这条路可行,但是要定制更多的 script。
2. Go 的 Benchmark 工具
Go 有自己的 unit test 和 Benchmark 的工具,见 https://golang.org/pkg/testing/
看了看使用方法,虽然是 Benchmark 工具,但是测试数据的准备、函数的调用方式和结果的判定基本上还是需要自己来定制一下。于是粗略地写了几个函数,分别用于启动 HTTP server 和 websocket server,准备测试数据,发送消息和接收消息。写了一个 BenchmarkHTTP 来大概检测了一下是否可以调用这几个函数来判断是否可行,嗯可行。
3. Go 的 profiling 工具
在第二步走通了测试方法,粗略地检测了一下性能,让人很遗憾地发现大概只有 1500 的 QPS。于是想了一下是不是 go 有 profiling 工具检查一下到底是谁用的时间长。找了找还真有 https://golang.org/pkg/net/http/pprof/ 。另外还有 runtime 的 pprof https://golang.org/pkg/runtime/pprof/ 。
具体做法也很简单
import _ "net/http/pprof"
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
看 heap 的使用情况
30s 内 CPU 的使用情况
收集 5s 内的 trace
在经过分析和 review 代码的时候,发现 map 的所有操作都没有加锁这一隐患,于是加锁,调整测试代码,再跑,QPS 终于到了一个令人信服的结果。