这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
一、高质量编程
什么是高质量——编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码
- 高质量代码编程原则
- 简单性:消除“多余的复杂”,方便理解修复改进
- 可读性:确保代码可读
- 生产力:团队整体工作效率非常重要
编码规范
- 代码格式:推荐使用gofmt自动化格式代码,也有部分使用goimports
- 注释:
- 注释应该解释代码作用:适合注释公共符号,避免重复解释,尽量提供有效信息
- 代码如何做的:适合注释实现过程,对一些复杂难读的实现逻辑进行说明
- 代码实现原因:适合解释的外部因素,提供额外的上下文
- 代码什么情况会出错:适合解释代码的限制条件
- 命名规范:
- 简洁
- 缩略词全大写,如ServerHTTP
- 变量距离其被使用的地方越远,则需携带更多的上下文信息
- 函数名不携带包名信息
- 包名只由小写字母组成,简洁并包含上下文信息,不使用常用变量,使用单数
- 控制流程:
- 避免嵌套,保持流程清晰
- 优先处理错误/特殊情况,尽早返回或继续循环来减少嵌套
- 尽量保持正常代码为最小缩进
- 错误和异常处理
- 简单错误
- 出现一次的错误,其他地方无需捕获
- 优先使用
errors.New来创建匿名变量表示简单错误 - 如果有格式化需求,使用
fmt.Errorf
- 错误的Warp和Unwarp
- 一个error嵌套另一个error,从而生成error的跟踪链
- 在fmt.Errorf中使用:
%w关键字来将一个错误关联至错误链中 - 判断错误链上是否有特定错误,使用
errors.Is - 在错误链上获取特定种类的错误,使用
errors.As,可以获取到text信息
- panic
- 程序异常被叫做panic,当panic被抛出异常后,如果我们没有在程序中添加任何保护措施的话,程序就会打印出panic的详细情况之后,终止运行
- 不推荐在业务代码中使用panic,若问题可以被屏蔽或解决,建议使用error代替panic
- recover
- 使当前程序从运行时的panic状态中恢复并重新获得流程控制权,如果调用了内置函数recover,并且定义该defer语句的函数发生了panic异常,recover会使用程序从panic中恢复,并且返回panic value,导致panic异常的函数不会继续执行,但能正常返回。在未发生panic时调用recover,recover会返回nil。
recover只有在defer调用的函数中有效- 嵌套无法生效
- 只在当前goroutine生效
- defer语句是后进先出
- 在log中记录当前的调用栈,
debug.Stack()
- 简单错误
defer func(){
if e := recover(); e != nil{
f = nil
err = fmt.Errorf("gitfs panic: %v\n%s", e , debug.Stack() )
}
}
性能优化
go提供了基准性能测试的benchmark工具 go test -bench
- slice 预分配内存
- 尽可能在使用make()初始化切片时提供容量信息
- 在已有切片基础上切片,不会创建新的底层数组,可使用
cppy代替re-slice
- map预分配内存
- 字符串处理
- 使用
strings.Bulider,比bytes.buffer快一些,Grow可以预分配内存
- 使用
- 空结构体
- 空结构体struct{}实例不占用内存空间
- 可用做占位符使用
- 实现set,可以考虑用map空结构体
- atomic包
- 原子性
二、性能调优实战
原则:依赖于数据进行优化,不要过早和过度优化,定位最大瓶颈
性能分析工具——pprof
go tool "http://xxx?seconds=10" 采集服务的性能数据,seconds为采集时长
- 命令
topN:查看占用资源最多的函数
Flat==Cum:函数中未调用其他函数
Flat==0:函数中只有其他函数调用
list:根据指定的正则表达式查找代码行web:调用关系可视化heap:堆内存分析,在url后加上heap,go tool pprof -http=:8080 "http://xxx/heap"goroutine:协程分析,在url后加上goroutine,go tool pprof -http=:8080 "http://xxx/goroutine"mutex:锁分析,在url后加上mutex,go tool pprof -http=:8080 "http://xxx/mutex"bolck:阻塞分析,在url后加上bolck,go tool pprof -http=:8080 "http://xxx/bolck"
切换不同采样说明
- alloc_objects:程序累计申请的对象数
- inuse_objects:程序当前持有的对象数
- alloc_space:程序累计申请的内存大小
- inuse_space:程序当前占用的内存大小