这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记
代码规范和性能测试
什么是高质量的代码
- 正确可靠
- 简洁清晰
- 无性能隐患
编码规范
1. 注释
单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾。
// 单行注释
/*
Author by sad
我是多行注释
*/
什么时候写注释
- 注释应该解释代码作用
- 注释应该解释代码如何做的
- 注释应该解释代码实现的原因
- 注释应该解释代码什么情况会出错
- 公共符号始终要注释:变量、常量、函数以及结构都需要添加注释
2. 命名规范
-
变量
简洁胜于冗长.
变量距离其被使用的地方越远,则需要携带越多的上下文信息
全局变量在其名字中需要更多的上下文信息,使得在不同地方可以轻易辨认出其含义
变量命名一般采用驼峰式,当遇到特有名词(缩写或简称,如DNS)的时候,特有名词根据是否私有全部大写或小写。 var apiClient var URLString
-
常量
同变量规则,力求语义表达完整清楚,不要嫌名字长。 如果模块复杂,为避免混淆,可按功能统一定义在package下的一个文件中。
-
结构体命名规范
结构体名应该是名词或名词短语,如Account,Book,避免使用Manager这样的。如果该数据结构需要序列化,如json, 则首字母大写, 包括里面的字段。
-
函数
采用驼峰式。将功能及必要的参数体现在名字中, 不要嫌长, 如updateById,getUserInfo. 如果包外不需要访问请用小写开头的函数 如果需要暴露出去给包外访问需要使用大写开头的函数名称
-
包
包名用小写,使用短命名,尽量和标准库不要冲突。包名统一使用单数形式。
-
文件命名规范 由于文件跟包无任何关系, 而又避免windows大小写的问题,所以推荐的文件规范如下:
文件名应一律使用小写, 不同单词之间用下划线分割, 不用驼峰式,命名应尽可能地见名知意。尽量见名思义,看见文件名就可以知道这个文件下的大概内容.其中测试文件以test.go结尾,除测试文件外,命名不出现。
3.错误处理
简单错误处理
优先使用 errors.New 来创建匿名变量来直接表示该错误。有格式化需求时使用 fmt.Errorf
错误的 Wrap 和 Unwrap
在 fmt.Errorf 中使用 %w 关键字来将一个错误 wrap 至其错误链中
错误判定
使用 errors.Is 可以判定错误链上的所有错误是否含有特定的错误。
在错误链上获取特定种类的错误,使用 errors.As
panic
不建议在业务代码中使用 panic
调用函数都不包含 recover 就会造成整个程序崩溃
如果问题可以屏蔽或者解决,建议使用error代替panic
当程序启动阶段发生不可逆转的错误时,可以在 init 或 main 函数中使用 panic
recover
recover 只能在被 defer 的函数中使用
嵌套无法生效,只在当前 goroutine 生效
defer是先进后出的
如果需要更多的上下文信息,可以 recover 后在 log 中记录当前的调用栈。
小结
代码的命名尽可能规范,要尽可能达到见名知意,简短而意思清楚
针对普通应用代码,在追求程序的性能之前,先保证程序的正确和可靠稳定优先,要对代码进行完整和规范的代码测试再发布提交
性能调优
1.性能调优原则
- 要依靠数据不是猜测
- 要定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
性能优化建议-Benchmark
性能表现需要实际的数据衡量
Go语言提供了支持基准性能测试工具的benchmark工具
benchmark函数以Benchmark开头而非 Test开头.
通过为go test命令添加-bench标记的方式运行benchmark测试,参数的值是一个正则表达式,如果运行全部benchmark测试,则指定为-bench=.即可;
为了避免普通的测试case被执行,可以加上-run=^$(匹配空函数)来跳过执行;
如果希望同步观察内存使用,可以通过-benchmem标记位来实现;
为了确保结果的一致性,可以加上-count参数来重复运行benchmark;
例子:
第一列是执行的函数名称,第二列是总的执行次数,第三列是平均运行时间;
func benchmarkFib(i int, b *testing.B) {
for n := 0; n < b.N; n++ {
Fib(i)
}
}
func BenchmarkFib1(b *testing.B) { benchmarkFib(1, b) }
func BenchmarkFib2(b *testing.B) { benchmarkFib(2, b) }
func BenchmarkFib3(b *testing.B) { benchmarkFib(3, b) }
func BenchmarkFib10(b *testing.B) { benchmarkFib(10, b) }
func BenchmarkFib20(b *testing.B) { benchmarkFib(20, b) }
func BenchmarkFib40(b *testing.B) { benchmarkFib(40, b) }
BenchmarkFib1 1000000000 2.84 ns/op
BenchmarkFib2 500000000 7.92 ns/op
BenchmarkFib3 100000000 13.0 ns/op
BenchmarkFib10 5000000 447 ns/op
BenchmarkFib20 50000 55668 ns/op
BenchmarkFib40 2 942888676 ns/op
性能优化建议-slice 预分配内存
- 在尽可能的情况下,在使用 make() 初始化切片时提供容量信息,特别是在追加切片时
2. 性能分析工具
性能调优的核心是性能瓶颈的分析,对于 Go 应用程序,最方便的就是 pprof 工具
pprof简介
pprof 是一个强大的性能分析工具,可以捕捉到多维度的运行状态的数据,下面简单介绍一下pprof的用法。 golang在语言层面集成了profile采样工具,在程序运行过程中可以获取cpu、heap、block、traces等执行信息,这些会涉及到runtime/pprof、net/http/pprof、runtime/trace等package
pprof 实践
排查 CPU 问题
-
命令行分析
- go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
-
top 命令
-
list 命令
-
熟悉 web 页面分析
-
调用关系图,火焰图
-
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/cpu"
排查堆内存问题
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
排查协程问题
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"
排查锁问题
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"
排查阻塞问题
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"
性能优化的小结
性能优化的前提是满足正确可靠,简洁清晰的质量因素..性能优化是综合评估,有时和时间效率,空间效率是对立的
针对不同语言,有不同的优化策略
\