高质量编程与性能调优
高质量定义: 编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码
三个考量
- 是否考虑齐全边界条件
- 对异常情况如何处理,能否保证稳定性
- 是否易读易维护(对别人而言,也是对自己而言)
Go语言开发者Dave Cheney认为的编程三大原则:
- 简单性 (简单直接清晰的逻辑来编写代码)
- 可读性 (自己和别人是否都可读)
- 生产力 (团队整体效率胜过单打独斗) (三大原则适用于任何编程语言)
五大方向确定Go代码高质量程度
- 代码格式
- 注释
- 命名规范
- 控制流程
- 错误和异常处理
代码格式
public的变量,常量,函数以及结构都要添加注释 任何及不明显也不简短的公共功能必须加注释 库中任何函数无论长短复杂程度都必须加注释
Go语言官方提供工具: gofmt:自动格式化Go语言代码为官方统一风格 goimports: 等于gofmt加上依赖包管理,自动增删依赖的包应用、将依赖包按字母序排序并分类
注释
注释四个作用:
- 解释代码作用
- 注释公共符号
- 解释代码如何做的
- 实现过程
- 解释代码实现的原因
- 解释代码的外部因素
- 提供额外上下文
- 解释代码什么情况会出错
- 解释代码的限制条件
命名规范
Variable
- 简介胜过冗长
- 缩写词全大写(ServeHTTP),如果不需要导出,使用全小写(XMLHTTPRequest 或xmlHTTPRequest)
- Variable距离被使用的地方越远,命名中要携带越多上下文信息,使不同地方可以轻易便认出含义
Function
- 函数名不加package name的上下文信息
- 函数名尽量简短
- 当名为foo的包某个函数返回Type为Foo时,可以省略类型信息而不导致歧义
- 当名为foo的包某个函数返回Type为T(非Foo)时,可以在函数名中加入类型信息
package
- 只由小写字母组成,不包含大写字母和下划线等符号
- 简短并包含一定的上下文联系,例如schema、task
- 不要与标准库同名
- 以下规则尽量满足
- 不使用常用变量名作为包名
- 使用单数而不是复数
- 谨慎使用缩写
控制流程
避免嵌套,保持正常流程清晰(比如if就return的话,就不用再else) 尽量保持正常代码路径为最小缩进(优先处理错误情况、特殊情况,尽早return或continue loop来减少嵌套) 故障问题大多出现在复杂的条件语句和循环语句中
错误和异常处理
- 简单错误(仅出现一次error,且不需要catch error):
- 优先使用errors.New来创建匿名变量来直接表示简单错误
- 如果有格式化的需求,使用fmt.Errorf
- 复杂错误/错误的Wrap和Unwrap(进行包装和解包):
- 错误的Wrap提供了一个error嵌套另一个error的能力,从而生成一个error的追踪链
- 在fmt.Errorf中使用%w关键字来讲一个错误关联至错误链中 example:
list, _, err := c.GetBytes(cache.Subkey(a.actionID, "srcfiles")) if err != nil{ return fmt.Errorf("reading srcfiles list: %w", err) } - 错误判定
- 判定一个错误是否为特定错误,使用errors.Is
- 不同于使用==,该方法可以判定错误链上所有的错误是否含有特定的错误
data, err = lockedfile.Read(targ) if errors.Is(err, fs.ErrNotExist){ //Treat non-existent as empty, to bootstrap the "latest" file // the first time we connect to a given database. return []byte{}, nil } return data, err- 在错误链上获取特定种类的错误,使用errors.As
// 抓到pathError这一类 if errors.As(err, &pathError){ // 解析打印出来出现error的具体path fmt.Println("Failed at paht:", pathError.Path) } - Panic*
- 用于真正异常的情况(程序崩溃时)
- 不要使用panic, 如果真要用,必须用recover,不然会造成程序崩溃
- 当程序启动阶段发生不可你转的错误时,可以在init或main函数中使用panic
- Recover*
- 只能在被defer的函数中使用
- 嵌套无法生效
- 只在当前goroutine生肖
- defer的语句是后进先出
- 目的是recover在log中记录panic后,当前的调用栈
性能优化建议
- 性能量化工具:Benchmark,Go提供了支持基准性能测试的benchmark工具
go test -bench=. -benchmem - Slice,map等数据结构,对内存尽量提前预估,预分配内存,尽量避免扩容。
- 字符串处理,用string builder或者byte builder,两者都是对byte进行操作。不要用string+=进行操作,因为string是immutable type,每次都会重新allocate memory加上复制旧data
- 尽量使用空结构体struct{}节省内存,instance不占内存
- 使用atomic进行多线程操作
- 越高级的性能优化手段越容易出现问题
性能调优原则
- 依靠数据不是猜测
- 定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
性能分析工具pprof 可视化和分析性能和数据的工具,可以知道程序在什么地方耗费多少CPU,内存