作者:trumanyan,腾讯 CSIG 后台开发工程师
项目背景
网关服务作为统一接入服务,是大部分服务的统一入口。为了避免成功瓶颈,需要对其进行尽可能地优化。因此,特别总结一下 golang 后台服务性能优化的方式,并对网关服务进行优化。
技术背景:
- 基于 tarsgo 框架的 http 接入服务,下游服务使用 tarsgo 协议进行交互
性能指标
网关服务本身没有业务逻辑处理,仅作为统一入口进行请求转发,因此我们主要关注下列指标
- 吞吐量:每秒钟可以处理的请求数
- 响应时间:从客户端发出请求,到收到回包的总耗时
定位瓶颈
一般后台服务的瓶颈主要为 CPU,内存,IO 操作中的一个或多个。若这三者的负载都不高,但系统吞吐量低,基本就是代码逻辑出问题了。
在代码正常运行的情况下,我们要针对某个方面的高负载进行优化,才能提高系统的性能。golang 可通过 benchmark 加 pprof 来定位具体的性能瓶颈。
benchmark 简介
go test -v gate_test.go -run=none -bench=. -benchtime=3s -cpuprofile cpu.prof -memprofile mem.prof
- -run 知道单次测试,一般用于代码逻辑验证
- -bench=. 执行所有 Benchmark,也可以通过用例函数名来指定部分测试用例
- -benchtime 指定测试执行时长
- -cpuprofile 输出 cpu 的 pprof 信息文件
- -memprofile 输出 heap 的 pprof 信息文件。
- -blockprofile 阻塞分析,记录 goroutine 阻塞等待同步(包括定时器通道)的位置
- -mutexprofile 互斥锁分析,报告互斥锁的竞争情况
benchmark 测试用例常用函数
- b.ReportAllocs() 输出单次循环使用的内存数量和对象 allocs 信息
- b.RunParallel() 使用协程并发测试
- b.SetBytes(n int64) 设置单次循环使用的内存数量
pprof 简介
生成方式
- runtime/pprof: 手动调用如
runtime.StartCPUProfile
或者runtime.StopCPUProfile
等 API 来生成和写入采样文件,灵活性高。主要用于本地测试。 - net/http/pprof: 通过 http 服务获取 Profile 采样文件,简单易用,适用于对应用程序的整体监控。通过 runtime/pprof 实现。主要用于服务器端测试。
- go test: 通过
go test -bench . -cpuprofile cpuprofile.out
生成采样文件,主要用于本地基准测试。可用于重点测试某些函数。
查看方式
go tool pprof [options][binary] ...
- --text 纯文本
- --web 生成 svg 并用浏览器打开(如果 svg 的默认打开方式是浏览器)
- --svg 只生成 svg
- --list funcname 筛选出正则匹配 funcname 的函数的信息
- -http=":port" 直接本地浏览器打开 profile 查看(包括 top,graph,火焰图等)
go tool pprof -base profile1 profile2
- 对比查看 2 个 profile,一般用于代码修改前后对比,定位差异点。
通过命令行方式查看 profile 时,可以在命令行对话中,使用下列命令,查看相关信息
flat flat% sum% cum cum%
5.95s 27.56% 27.56% 5.95s 27.56% runtime.usleep
4.97s 23.02% 50.58% 5.08s 23.53% sync.(*RWMutex).RLock
4.46s 20.66% 71.24% 4.46s 20.66% sync.(*RWMutex).RUnlock
2.69s 12.46% 83.70% 2.69s 12.46% runtime.pthread_cond_wait
1.50s 6.95% 90.64% 1.50s 6.95% runtime.pthread_cond_signalflat: 采样时,该函数正在运行的次数*采样频率(10ms),即得到估算的函数运行”采样时间”。这里不包括函数等待子函数返回。
flat%: flat / 总采样时间值
sum%: 前面所有行的 flat% 的累加值,如第三行 sum% = 71.24% = 27.56% + 50.58%
cum: 采样时,该函数出现在调用堆栈的采样时间,包括函数等待子函数返回。因此 flat <= cum
cum%: cum / 总采样时间值
topN [-cum] 查看前 N 个数据:
- list ncname 查看某个函数的详细信息,可以明确具体的资源(cpu,内存等)是由哪一行触发的。
pprof 接入 tarsgo
服务中 main 方法插入代码
cfg := tars.GetServerConfig()
profMux := &tars.TarsHttpMux{}
profMux.HandleFunc("/debug/pprof/", pprof.Index)
profMux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
profMux.HandleFunc("/debug/pprof/profile", pprof.Profile)
profMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
profMux.HandleFunc("/debug/pprof/trace", pprof.Trace)
tars.AddHttpServant(profMux, cfg.App+"."+cfg.Server+".ProfObj")taf 管理平台中,添加 servant:ProfObj (名字可自己修改)
发布服务
查看 tasrgo 服务的 pprof
保证开发机能直接访问到 tarsgo 节点部署的 ip 和 port。
查看 profile(http 地址中的 ip,port 为 ProfObj 的 ip 和 port)
# 下载cpu profile
go tool pprof http://ip:port/debug/pprof/profile?seconds=120 # 等待120s,不带此参数时等待30s
# 下载heap profile
go tool pprof http://ip:port/debug/pprof/heap
# 下载goroutine profile
go tool pprof http://ip:port/debug/pprof/goroutine
# 下载block profile
go tool pprof http://ip:port/debug/pprof/block
# 下载mutex profile
go tool pprof http://ip:port/debug/pprof/mutex
# 下载20秒的trace记录(遇到棘手问题时,查看trace会比较容易定位)
curl http://100.97.1.35:10078/debug/pprof/trace?seconds=20 > trace.out
go tool trace trace.out 查看直接在终端中通过 pprof 命令查看
sz 上面命令执行时出现的
Saved profile in /root/pprof/pprof.binary.alloc_objects.xxxxxxx.xxxx.pb.gz
到本地在本地环境,执行
go tool pprof -http=":8081" pprof.binary.alloc_objects.xxxxxxx.xxxx.pb.gz
即可直接通过http://localhost:8081页面查看。包括topN,火焰图信息等,会更方便一点。
GC Trace
golang 具备 GC 功能,