准备工作
下载 go-wrk(an HTTP benchmarking tool)
GitHub:github.com/tsliwowicz/…
安装
go install github.com/tsliwowicz/go-wrk@latest
基本使用
使用 80 个 Go 协程(连接)运行 5 秒钟的基准测试:
./go-wrk -c 80 -d 5 http://192.168.1.118:8080/json
输出:
Running 10s test @ http://192.168.1.118:8080/json
80 goroutine(s) running concurrently
142470 requests in 4.949028953s, 19.57MB read
Requests/sec: 28787.47
Transfer/sec: 3.95MB
Avg Req Time: 0.0347ms
Fastest Request: 0.0340ms
Slowest Request: 0.0421ms
Number of Errors: 0
性能分析
开启性能分析
开启性能分析只需要导入下面这个包即可:
import _ "net/http/pprof"
pprof 包通过它的 HTTP 服务端提供 pprof 可视化工具期望格式的运行时剖面文件数据服务。
本包一般只需导入获取其注册 HTTP 处理器的副作用。处理器的路径以 /debug/pprof/ 开始。
压力测试
启动 main.go
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"os"
"path/filepath"
"time"
"github.com/varstr/uaparser"
)
const hostPort = ":8080"
func main() {
http.HandleFunc("/hello", Hello)
fmt.Println("Starting server on", hostPort)
if err := http.ListenAndServe(hostPort, nil); err != nil {
log.Fatalf("HTTP server failed: %v", err)
}
}
func Hello(w http.ResponseWriter, r *http.Request) {
start := time.Now()
tags := getStatsTags(r)
duration := time.Since(start)
fmt.Println(tags, duration)
}
func getStatsTags(r *http.Request) map[string]string {
userBrowser, userOS := parseUserAgent(r.UserAgent())
stats := map[string]string{
"browser": userBrowser,
"os": userOS,
"endpoint": filepath.Base(r.URL.Path),
}
hostName, _ := os.Hostname()
if hostName != "" {
stats["host"] = hostName
}
return stats
}
func parseUserAgent(uaString string) (browser, os string) {
ua := uaparser.Parse(uaString)
if ua.Browser != nil {
browser = ua.Browser.Name
}
if ua.OS != nil {
os = ua.OS.Name
}
return browser, os
}
使用「go-wrk」进行压测
go-wrk -d 500 http://localhost:8080/hello
Web 监控
浏览器访问:http://localhost:8080/debug/pprof/
简单查看,以 goroutine 为例,参数有 debug=1 与 debug = 2:
采样分析
下面命令会打开一个交互页面:
go tool pprof --seconds 5 http://localhost:8080/debug/pprof/profile
常用命令:
top n:n 不写默认显示 10 个占用 CPU 时间最多的函数。top -cum:将数据累计查看各个函数 CPU 占用。tree:树形结构查看 goroutine 情况。list 方法名:查看方法名里面具体调用耗时时长。web:生成 SVG 函数调用图(需安装graphviz)。exit:退出分析。
打开 pprof 可视化页面,与上面可交互页面中 web 命令效果一样:
go tool pprof -http=:8888 http://localhost:8080/debug/pprof/profile?seconds=60
链路图:
火焰图:
其他参数:
-inuse_space:分析程序常驻内存的占用情况-alloc_objects:分析内存的临时分配情况
go tool pprof -inuse_space http://localhost:8080/debug/pprof/heap
go tool pprof -alloc_space http://localhost:8080/debug/pprof/heap
链路追踪分析
curl http://localhost:8080/debug/pprof/trace?seconds=30 > trace.out
go tool trace trace.out
上面的命令会打开一个 Web 页面:
点击
View trace 可以看到整个链路追踪页面。 按 W 可以将时间线放大,S 将时间线缩小。
Goroutine analysis:查看每个方法的 Goroutine 数量。Network blocking profile:查看 IO 阻塞情况。Synchronization blocking profile:查看系统同步阻塞情况。Syscall blocking profile:查看系统调用阻塞情况。Scheduler latency profile:查看调度程序延迟情况。
如果只想针对某个方法进行分析可以在方法内第一行加上下面代码:
f, err := os.Create("trace.out")
if err != nil {
panic(err)
}
defer f.Close()
err = trace.Start(f)
if err != nil {
panic(err)
}
defer trace.Stop()
常用解释:
| 名称 | 含义 |
|---|---|
| Execution Time | 执行时间 |
| Network Wait Time | 网络等待时间 |
| Sync Block Time | 同步阻塞时间 |
| Blocking Syscall Time | 调用阻塞时间 |
| Scheduler Wait Time | 调度等待时间 |
| GC Sweeping | GC 清扫 |
| GC Pause | GC 暂停 |
打点对比分析
下文以
goroutine为例,想比较「内存」的话,可把 url 后缀改成heap。
打第一个时间点:
go tool pprof http://localhost:8080/debug/pprof/goroutine
等待一会,再打第二个时间点:
go tool pprof http://localhost:8080/debug/pprof/goroutine
会生成两个采样文件:
pprof.goroutine.001.pb.gzpprof.goroutine.002.pb.gz
对比分析:
go tool pprof -base pprof.goroutine.001.pb.gz pprof.goroutine.002.pb.gz
会和之前一样出现一个命令行交互界面,不同的是这个里面的信息是两者的差异比较。可以通过 top 查看两者 goroutine 差异最大之处是在哪里,然后通过 traces 查看栈调用信息,也可以通过 list 方法名 查看某个方法具体哪一行出了问题。
其他
生成 pprof.cpu 文件
// hello_test.go
import (
"fmt"
"testing"
)
func BenchmarkHello(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Sprintf("hello")
}
}
go test -bench . -benchmem -cpuprofile pprof.cpu
分析 pprof.cpu 文件
go tool pprof pprof.cpu
使用 ab 压测(ab - Apache HTTP server benchmarking tool)
ab 的原理:ab 命令会创建多个并发访问线程,模拟多个访问者同时对某一 URL 地址进行访问。它的测试目标是基于 URL 的,因此,它既可以用来测试 apache 的负载压力,也可以测试 nginx、lighthttp、tomcat、IIS 等其它 Web 服务器的压力。
ab 命令对发出负载的计算机要求很低,它既不会占用很高 CPU,也不会占用很多内存。但却会给目标服务器造成巨大的负载,其原理类似「CC攻击」。自己测试使用也需要注意,否则一次上太多的负载。可能造成目标服务器资源耗完,严重时甚至导致死机。
ab -n 200 -c 20 -p data.txt http://localhost:8080/hello
-n:一共请求多少次。-c:每次请求多少个。