桔妹导读:线上性能问题的定位和优化是程序员进阶的必经之路,定位问题的方式有多种多样,常见的有观察线程栈、排查日志和做性能分析。性能分析(profile)作为定位性能问题的大杀器,它可以收集程序执行过程中的具体事件,并且对程序进行抽样统计,从而能更精准的定位问题。本文会以 go 语言的 pprof 工具为例,分享两个线上性能故障排查过程,希望能通过本文使大家对性能分析有更深入的理解。
在遇到线上的性能问题时,面对几百个接口、成吨的日志,如何定位具体是哪里的代码导致的问题呢?这篇文章会分享一下 profiling 这个定位性能问题的利器,内容主要有:
如何通过做 profiling 来精准定位故障源头
两个工作中通过 profiling 解决性能问题的实际例子
总结在做 profiling 时如何通过一些简单的现象来快速定位问题的排查方向
日常 golang 编码时要避开的一些坑
部分 golang 源码解析
文章篇幅略长,也可直接翻到下面看经验总结。
1.
profiling是什么
profile 一般被称为 性能分析,词典上的翻译是 概况(名词)或者 描述…的概况(动词)。对于计算机程序来说,它的 profile,就是一个程序在运行时的各种概况信息,包括 cpu 占用情况,内存情况,线程情况,线程阻塞情况等等。知道了程序的这些信息,也就能容易的定位程序中的问题和故障原因。
golang 对于 profiling 支持的比较好,标准库就提供了profile库 "runtime/pprof" 和 "net/http/pprof",而且也提供了很多好用的可视化工具来辅助开发者做 profiling。
2.
两次 profiling 线上实战
纸上得来终觉浅,下面分享两个在工作中实际遇到的线上问题,以及我是如何通过 profiling 一步一步定位到问题的。
▍cpu 占用 99%
某天早上一到公司就收到了线上 cpu 占用率过高的报警。立即去看监控,发现这个故障主要有下面四个特征:
cpu idle 基本掉到了 0% ,内存使用量有小幅度增长但不严重;
故障是偶发的,不是持续存在的;
故障发生时3台机器的 cpu 几乎是同时掉底;
故障发生后,两个小时左右能恢复正常。
现象如图,上为内存,下为cpu idle:
检查完监控之后,立即又去检查了一下有没有影响线上业务。看了一下线上接口返回值和延迟,基本上还都能保持正常使用,就算cpu占用 99% 时接口延时也只比平常多了几十ms。由于不影响线上业务,所以没有选择立即回滚,而是决定在线上定位问题(而且前一天后端也确实没有上线新东西)。
所以给线上环境加上了 pprof,等着这个故障自己复现。代码如下:
import _ "net/http/pprof"
func main() {
go func() {
log.Println(http.ListenAndServe("0.0.0.0:8005", nil))
}()
// ..... 下面业务代码不用动
}
golang 对于 profiling 的支持比较完善,如代码所示,只需要简单的引入 "net/http/pprof" 这个包