这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
一、高质量编程
1.1 高质量编程简介
正确可靠、简介清晰:
- 是否考虑到各种边界条件
- 异常处理情况,稳定保证性
- 易读可维护
1.1.1 编程原则
简单性:
- 以简洁的逻辑写代码
可读性:
- 需要让其他阅读代码的人易于理解
- 编写可维护的代码
生产力:
- 团队整体工作效率非常重要
1.2 编码规范
1.2.1 代码格式
Golang 提供了 gofmt 命令快速格式化 Golang 源代码
使用 Goland 也能做到,保存时直接格式化代码,优化导入等操作
1.2.2 注释
- 公共符号始终需要注释
注释需要做什么
- 解释代码作用:适用公共符号的注释
- 解释代码如何做的:适用实现过程的注释
- 解释代码实现的原因:解释代码的外部因素或者提供额外的上下文
- 解释代码在什么情况下会出错:解释代码的限制条件
1.2.3 命名规范
- 简介胜于冗长
- 缩略词全大写
- 使用时离定义较远时,携带更多的上下文信息
package:
- 只由小写字母组成,不包含大写字母和下划线
- 简短并包含一定的上下文信息
- 尽量不予标准库同名
- 尽量不是使用常用变量名
- 尽量使用单数而不是复数
- 谨慎使用缩写
1.2.3 控制流程
- 避免嵌套,保证正常的流程清晰
- 尽量保持正常代码路劲为最小路劲
1.2.4 错误和异常处理
简单错误
仅仅出现一次,且在其它地方不需要捕获该错误
优先使用 errors.New 创建匿名变量来直接表示简单错误
或者使用 fmt.Errorf 创建有格式的错误信息
错误 Wrap(包装) & Unwrap(解包)
通过 fmt.Errorf 的 %w 能将一个错误包装到另一个错误中
错误判定
判定一个错误是否为特定的错误,使用 errors.Is 方法(会检查错误链路中是否包含的特定的错误)
使用 errors.As 方法会获取链路上特定类型的错误
panic
因为 Golang 的规定了 Errors is values 但是遇到更严重的情况是,使程序无法正常工作时,使用 panic 函数,如果不使用 recover 会使函数崩溃
- 不建议在业务中使用 panic
- 若问题可以屏蔽或者解决可以使用 panic 代替
- 当一个程序启动阶段发生不可逆转的错误时可以在
init函数或者main函数中使用 panic
recover
类似 Java 程序的
catch
- 只能在 defer 中调用 recover 处理该方法或者该方法调用的方法产生的 panic
- 嵌套无法使用
- 只在当前的 goroutine 生效
- defer 的语句是后进先出的
1.3 性能优化建议
- 性能优化的前提是满足正确可靠、简洁清晰等质量因素
- 性能优化是综合评估,部分情况下还需要选择合适的时间效率或者空间效率来牺牲
1.3.1 Benchmark
使用方式为 go test -bench=. -benchmark
输出的各列数据表示为:
- 测试的函数名-GOMAXPROCS的值(1.5 之后默认为 CPU 核数,在容器环境下可能出现问题,可以使用
go.uber.org/automaxprocs解决) - 表示一共执行的次数,
b.N的值 - 每次执行花费的平均时间
- 每次执行花费的平均内存
- 每次执行申请多少次内存
1.3.2 Slice(切片)
- 尽可能使用 make 初始化时提供容量信息,减少在扩容是需要重新申请更大的内存空间在容纳新的数据,
- copy 是对切片深 copy,而 re-slice 是浅拷贝,会引用原切片的大数组,可能导致大内存未示范
1.3.3 map
- 同理在使用时,尽量预分配内存
提前分配好空间,能减少内存拷贝和 Rehash 的消耗
1.3.4 字符串拼接
尽量使用 strings.Builder 来进行复杂的字符串拼接
- 使用 + 来拼接字符串每次都会重新分配内存
strings.Builder和bytes.Buffer底层都是基于 byte 数组,内容扩容策略,不需要每次都拼接重新分配内容strings.Builder和bytes.Buffer都提供了 Grow 来初始化需要使用的内存大小
bytes.Buffer在转字符串时会整个重新申请一个新的内存,而strings.Builder直接将底层数组转为字符串返回
1.3.5 空结构体
空结构体 struct{} 不占用任何内存空间,可以在各种场景下作为占位符使用
使用 map 实现 set(Golang 没有提供),map 是无序的,使用 value 值的类型设置为 struct{},从而节省内存使用
1.3.6 atomic(原子包)
- 锁是通过操作系统来实现的,属于系统调用
- atomic 是通过硬件实现的,效率比使用锁高
- 使用
sync.Mutex应该是来保护一段逻辑,而不是保护一个字段 - 对于非数值操作可以使用
atomic.Value
二、性能调优实战
2.1 性能调优简介
性能调优的原则:
- 依靠数据而不是猜测
- 要定位的最大瓶颈而不是技术末节
- 不要或早或过度优化
2.2 性能分析工具 pprof
pprof 能可视化的分析性能数据的工具
一图导览: