这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
# Go高质量编程和编码规范
高质量编程
标准:正确可靠,简洁清晰
- 边界条件是否完备
- 异常情况处理,稳定性保证
- 易读易维护
编程原则: 简单性;可读性;生产力
如何编写高质量Go代码:
-
代码格式
- gofmt和goimports
-
注释
- 公共符号注释,向外部暴露的方法要详细注释
- 实现接口方法,是不需要注释的
- 注释应注释代码作用,如何做,实现的原因,什么情况会出错
-
命名规范
-
简洁胜于冗长
-
缩略词全大写,位于开头但不需要导出时,要全小写
-
全局变量多带上下文信息
-
Go语言中包和函数名是成对出现的,所以函数名应该不包括上下文信息,尽量简短。
-
包名尽可能简短,包含一定上下文信息,不包括大写字幕和下划线;不要和标准库同名。使用单数而不是复数。
-
-
控制流程
- 避免嵌套,如果两个分支均包含return,可以去掉冗余else
- 优先处理错误和特殊情况,代码尽早返回减少嵌套
-
错误处理和异常处理
- 简单错误,可以errors.New创建匿名错误;或者fmt.Errorf进行错误格式化输出
- 复杂错误,错误Warp提供error嵌套error的能力,可以通过fmt.Error("%w")来将错误关联到错误链
- 错误判断,errors.Is(err, 错误类型),判断错误链上是否有指定类型的错误
- 错误提取,可以定义一个错误类型的指针,将指针的地址传入error.As(err, &pointer)来将错误提取到pointer中
- panic,尽量少用;一般是init时,启动失败提前panic;程序运行中尽量不要
- recover,只能再defer中使用,且只在当前的协程生效;可以通过recover获取debug中的调用栈。
性能调优实战
性能评估:benchmark
slice预分配内存,尽量在make的时候就分够容量
在已有切片创建切片,会导致原切片引用的数组迟迟得不到释放,解决方法是使用copy
map预分配内存
字符串处理使用string.Builder拼接字符串性能更好;strings.Builder也可以使用通过builder.Grow(大小)来提前预分配内存
空结构体,struct{}不占用内存空间,可以作为占位符;比如使用map[keyType]struct{}来实现一个集合Set。此处值是不占用空间的。
多线程计数器,除了传统的加锁sync.Mutex的方式,还可以synb/atomic。atomic硬件实现,效率比锁高,更适合做计数器。sync.Mutex更适合保护一段代码逻辑。
性能优化分析工具 pprof
- 依靠数据而不是猜测
- 定位最大拼接而不是细枝末节
- 不宜过早优化
- 不应过度优化
pprof
- cpu,堆内存,协程,锁,阻塞,线程数据采样
- 网页,终端分析工具,各种可视化展示
- runtime/pprof,net/http/pprof
go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
top,查看占用情况,类似linux中的top web,调用关系可视化
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"