这是我参与「第五届青训营 」笔记创作活动的第 3 天
一、本课重点内容
- 编程规范
- 性能调优
二、详细知识点介绍
性能调优实战
- 简介
- 性能分析工具 pprof
- 性能调优案例
简介
性能调优原则:
- 要依靠数据而不是猜测
- 要定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
pprof 工具
当进行性能优化的时候,大家肯定想要知道应用在什么地方消耗的较大的资源(CPU、Memory等等)导致了性能瓶颈,
而pprof是用于可视化和分析性能数据的强大工具
golang在语言层面集成了profile采样工具,在程序运行过程中可以获取cpu、heap、block、traces等执行信息,这些会涉及到 runtime/pprof、net/http/pprof、runtime/trace等package
功能简介
获取数据
工具应用
主要使用runtime/pprof库,将数据写入文件中,适用于很小的程序
执行完后,会发现cpuprofile、memoryprofile文件,里面包含cpu、内存的画像。 runtime/pprof 直接支持这两种画像。
package main
import (
"fmt"
"os"
"runtime/pprof"
)
func main() {
//CPU Profile
f, err := os.Create("./cpuprofile")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
//Memory Profile
fm, err := os.Create("./memoryprofile")
if err != nil {
fmt.Println(err)
return
}
defer fm.Close()
pprof.WriteHeapProfile(fm)
for i := 0; i < 100; i++ {
fmt.Println("hello")
}
}
Web服务
我们可以通过引入net/http/pprof包,并在主函数中添加以下代码即可进行性能分析
使用默认 ServerMux,在代码中直接使用 http.ListenAndServe("0.0.0.0:8000",nil)(当第二个参数为nil时,需要匿名引用)
package main
import (
"fmt"
"net/http"
_ "net/http/pprof"
"strings"
)
func hello(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["param"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello 掘金!")
}
func main() {
http.HandleFunc("/", hello) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil {
fmt.Printf("ListenAndServe: %s", err)
}
}
执行上述代码后,访问 http://127.0.0.1:9090/?param=111 即可看见返回内容"Hello 掘金!"
此时访问 http://127.0.0.1:9090/debug/pprof/ 可以看见pprof工具为我们配置的调试页面
Gin框架
在gin中引入pprof工具非常简单!
只需要两步即可轻松使用pprof!
- 引入
gin-contrib/pprof - 调用
pprof.Register(router)
grpc服务器
上述的几种应用都能通过链接或本地访问,如果像grpc这种类型的服务该如何使用pprof进行性能分析?
pprof做CPU分析原理是按照一定的频率采集程序CPU(包括寄存器)的使用情况,所以我们可以
- 在
gRPC服务启动时,异步启动一个监听其他端口的HTTP服务,通过这个HTTP服务间接获取gRPC服务的分析数据 - 因为gin使用默认的
ServerMux(服务复用器),所以只要匿名导入net/http/pprof包,这个HTTP的复用器默认就会注册pprof相关的路由
小结
通过上述几种类型,看出应用服务核心在于将pprof的路由注册到服务中,并能够提供访问。
数据分析
到这里我们通过pprof获取到了性能相关的数据,可以存储在文件中或是在浏览器中显示,但我们该如何分析这些数据呢?
Golang在 1.11 版本后在自带的工具集 go tool中集成了pprof工具来分析由pprof库生成的数据文件。
使用go tool pprof分析数据,也有两种方式
-
go tool pprof [url(http://host:port/debug/pprof/profile)] 进入命令行交互模式
-
文件方式
- go tool pprof [文件名] 进入命令行交互模式
- go tool pprof -http=:9091 [文件名] 进入web分析页面
进入命令行交互模式后,可以使用help查看所有子命令,使用help <cmd|option>查看子命令使用方法。
使用profile可以获取很多重要信息,cpu profiling、memory profiling使用也是最频繁的。分析的时候,需要先获取到数据,通过web发现耗时的函数,然后通过list找到具体位置。
其它的数据的分析和CPU、Memory基本一致。下面列一下所有的数据类型:
- http://localhost:8082/debug/pprof/ :获取概况信息,即图一的信息
- go tool pprof http://localhost:8082/debug/pprof/allocs : 分析内存分配
- go tool pprof http://localhost:8082/debug/pprof/block : 分析堆栈跟踪导致阻塞的同步原语
- go tool pprof http://localhost:8082/debug/pprof/cmdline : 分析命令行调用的程序,web下调用报错
- go tool pprof http://localhost:8082/debug/pprof/goroutine : 分析当前 goroutine 的堆栈信息
- go tool pprof http://localhost:8082/debug/pprof/heap : 分析当前活动对象内存分配
- go tool pprof http://localhost:8082/debug/pprof/mutex : 分析堆栈跟踪竞争状态互斥锁的持有者
- go tool pprof http://localhost:8082/debug/pprof/profile : 分析一定持续时间内CPU的使用情况
- go tool pprof http://localhost:8082/debug/pprof/threadcreate : 分析堆栈跟踪系统新线程的创建
- go tool pprof http://localhost:8082/debug/pprof/trace : 分析追踪当前程序的执行状况
三、 课后个人总结
希望大家在生产环境中不会用到pprof,正常情况下,大部分问题都不需要用到pprof即可解决。使用pprof是有一定成本的,必须要想办法先能获取到数据。
如果真的遇到线上问题必须使用pprof,建议先想好要分析哪类数据。
pprof对程序的性能优化还是很有利的,获取数据后,可以快速定位到耗时较多的位置进行优化,而且也支持只打印和某个函数相关的命令,很人性化。