这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
01高质量编程
什么是高质量——编写的代码能够达到正确可靠,简洁清晰的目标可称之为高质量代码
边界问题
异常处理
易读易维护
1.1简介
编程原则:简单性 可读性 生产力
1.2 编码规范
代码格式 注释 命名规范 控制流程 错误和异常处理
公共符号始终要注释
代码格式:
gofmt goimports
注释应该做的:
注释应该解释代码的作用
注释应该解释代码如何做的
注释应该解释代码实现的原因
注释应该解释代码什么情况会出错
小结:
代码是最好的注释
注释应该提供代码未表达出的上下文信息。
命名规范:
变量名
简洁胜于冗长
缩略词全大写,当其位于变量开头不需要导出时,使用全小写。
变量距离其被使用的地方越远,则需要携带越多的上下文信息。
函数名
函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的。
函数名尽量简短。
当名为foo的包某个函数返回类型Foo时,可以省略类型信息而不导致歧义。
当名为foo的包某个函数返回类型T时,可以在函数名中加入类型信息。
包名
只由小写字母组成。不包含大写字母和下划线等字符。
简短并包含一定的上下文信息。如schema,task等。
不要与标准库同名。如不要使用sync或者strings
以下规则尽量满足,以标准库包名为例子。
不使用常用变量作为包名。
使用单数而不是复数。
谨慎地使用缩写。
控制流程
避免嵌套,保持正常流程清晰
尽量保持正常代码路径为最小缩进
错误和异常处理
简单错误
错误的Wrap和Unwrap
错误判定
不建议在业务代码中使用Panic
recover只能在被defer的函数中使用
error尽可能提供简明的上下文信息链,方便定位问题。
02性能调优实战
Benchmark
Slice
slice 预分配内存
尽可能在使用make()初始化切片时提供容量信息
大内存未释放
map预分配内存
字符串处理
使用strings.Builder,+拼接性能最差,bytes.Buffer与strings.Builder相近,strings.Buffer更快
使用空结构体节省内存
atomic包
算法问题 性能调优问题 没有明确边界
性能调优原则:
要依靠数据不是猜测
要定位最大瓶颈而不是细枝末节
不要过早优化
不要过度优化
性能分析工具 pprof
pprof-排查实战。
CPU
heap-堆内存
goroutine-协程
mutex-锁
block-阻塞
pprof-采样过程和原理
CPU:
采样对象:函数调用和它们占用的时间。
采样率:100次/秒,固定值。
采样时间:从手动启动到手动结束。
Heap-堆内存
采样程序通过内存分配器在堆上分配和释放的内存,记录分配/释放的大小和数量。
采样率:每分配512KMB记录一次,可在运行开头修改,1为每次分配均记录。
采样时间:从程序运行开始到采样时。
采样指标:alloc_space, alloc_objects, inuse_space, inuse_objects。
计算方式:inuse = alloc - free
Goroutine-协程&Thread Create-线程创建
Goroutine
记录所有用户发起并且在运行中的goroutine(即入口非runtime开头的)
runtime.main 的调用栈信息
Thread Create
记录程序创建的所有系统线程的信息
Block-阻塞 & Mutex-锁
阻塞操作
采样阻塞操作的次数和耗时
采样率:阻塞耗时超过阈值的才会被记录,1为每次阻塞均记录
锁竞争
采样争抢的次数和耗时
采样率:只记录固定比例的锁操作,1为每次加锁均记录
性能调优案例
业务服务优化
基础库优化
Go语言优化
流程
建立服务性能评估手段
分析性能数据,定位性能瓶颈
重点优化项改造
优化效果验证