这是我参与「第三届青训营-后端场」笔记创作活动的第2篇笔记。
高质量编程
常见编码规范
-
代码格式
- gofmt
- goimports
-
注释
-
应该
- 解释代码作用
- 解释如何做的
- 解释代码实现原因
- 解释代码在什么情况下会出错
-
原则
- 包中声明的每个公共符号(变量、常量、函数、结构)始终要注释
- 公共功能必须注释
- 对库中任何函数都必须进行注释
-
小结
- 代码是最好的注释
- 注释提供代码未表达出的上下文信息
-
-
命名规范
- 变量
- 函数
- 包
- 控制流程
- 错误和异常处理
性能优化建议
- 基准性能测试--benchmark工具
-
slice
- 预分配内存
- 释放未使用的内存
-
map
- 预分配内存
-
字符串处理
- 字符串拼接用
strings.Builder
- 字符串拼接用
-
空结构体
- 空结构体
struct{}实例不占据任何内存空间,可用作占位符
- 空结构体
-
atomic包
- 锁通过操作系统实现,属于系统调用,而atomic通过硬件实现,效率比锁高
性能调优
简介
-
原则
- 依靠数据而不是猜测
- 定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
性能分析工具pprof
功能说明
- 说明:用于可视化和分析性能分析数据的工具
-
功能
-
分析
- 网页
- 可视化终端
-
工具
- runtime/pprof
- net/http/pprof
-
采样
- CPU
- 堆内存-Heap
- 协程-Goroutine
- 锁-Mutex
- 阻塞-Block
- 线程创建-ThreadCreate
-
展示
- 列表-List
- Top
- 调用图-Graph
- 火焰图-FlameGraph
- Peek
- 源码-Source
- 反汇编-Disassemble
-
实际案例
-
准备工作
- pprof测试项目链接:埋入一些炸弹代码,产生可观测的性能问题
-
使用pprof定位性能问题点
-
进入pprof测试项目文件夹,运行
go run main.go -
-
展示字段说明
- allocs:内存分配情况
- blocks:阻塞操作情况
- cmdline:程序启动命令
- goroutine:当前所有goroutine的堆栈信息
- heap:堆上内存使用情况
- mutex:锁竞争操作情况
- profile:CPU占用情况
- threadcreate:当前创建的所有系统线程的堆栈信息
- trace:程序运行跟踪信息
-
浏览器直接查看指标时可读性很差
-
-
CPU资源占用过高问题排查定位
-
查看程序进程的资源占用情况
-
-
启动采样得到pprof采样结果
- 途径1 - 直接点击"profile"打开链接,默认会启动一个60s的采样,时间结束后会下载文件
- 途径2 -
go tool pprof "``http://localhost:6060/debug/pprof/profile?``seconds=10"会自动下载profile文件并分析
-
使用
go tool pprof命令分析profile文件-
下载并分析profile文件,进入pprof终端
-
-
使用top/topN命令查看占用CPU资源最多的函数
-
-
指标
- flat-当前函数本身的执行耗时
- falt%-flat占CPU总时间的比例
- sum%-上面每一行的flat%的总和
- cum-当前函数本身加上其调用函数的总耗时
- cum%-cum占CPU总时间的比例
-
说明
- 默认展示资源占用最高的10个函数;使用命令topN可以只查看资源占用最高的N个函数
- flat == cum: 函数中没有调用其他的函数
- flat == 0:函数中只有其他函数的调用
-
使用list命令根据指定的正则表达式查找代码行
-
-
从图中结果可以看出,有一处100亿次的空循环,问题定位成功
-
使用web命令生成调用关系图
- Windows下直接下载可执行文件安装,但安装完成后记得要重启,否则环境变量会不生效
-
- 调用关系图中,会显示每个节点的资源占用情况,最大的方框即为定位出的CPU“炸弹”。因此,我们将这段引起CPU资源占用异常的问题代码注释掉。
- 重新运行程序,可以发现进程CPU占用资源已经降下来了。但,内存使用还是很高,所以接下来排查内存使用异常的问题。
-
-
-
Heap-堆内存使用问题排查定位
- 使用命令
go tool pprof -http=:8080 "``http://localhost:6060/debug/pprof/heap``",通过-http=:8080参数,可以开启pprof自带的Web UI,可以看到内存占用情况。在VIEW中还可以切换不同的视图。 - 可以看出,内存使用异常出现在上图代码,故将其注释掉。
- 至此,内存使用异常“炸弹”已排除。
- 使用命令
-
Goroutine问题排查定位
-
使用命令
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"- 节点图
- 火焰图
- 火焰图中,由上到下表示调用顺序;每一块代表了一个函数,越长代表占用CPU的时间越长;火焰图是动态的,支持点击块进行分析。
- 可以看到(*Wolf).Drink这个调用创建了超过90%的Goroutine,于是将视图切换至源码页面,通过正则搜索出Wolf相关函数。
- 可以定位到问题是没有及时退出Goroutine。
-
-
Mutex问题排查定位
- 使用命令
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex" - 可以发现Goroutine足足等待了1秒才解锁,因此导致锁竞争的产生。将等待的1秒注释后,锁的竞争导致的阻塞时间显著减少。
- 使用命令
-
Block问题排查定位
- 使用命令
go tool pprof -http=:8080 "``http://localhost:6060/debug/pprof/block``" 在程序中,除了锁的竞争会导致阻塞之外,很多逻辑如读取一个Channel也会导致堵塞。
- 使用命令
-