高质量编程
简介
编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码。
- 各种边界条件是否考虑完备
- 异常情况处理,稳定性保证
- 易读易维护
编码原则
简单性、可读性、生产力。
- 代码格式
- 注释
- 命名规范
- 控制流程
- 错误和异常处理
编写规范
代码格式
推荐使用gofmt自动格式化代码。
注释
- 解释代码作用(注释公共符号)、解释代码如何做的(注释实现过程)、解释代码实现的原因(注释解释代码的外部因素、提供额外上下文)、解释代码什么情况会出错(解释代码的限制条件)。
- 公共符号必须要注释。
- 代码是最好的注释,注释应该提供代码未表达出的上下文信息。
命名规范
- variable:简洁、缩略词全大写(变量开头全小写)、变量距离其使用的地方越远,则需要携带越多的上下文信息。
- function:尽量简短、不携带包名的上下文信息,因为包名和函数名总是成对出现的。
- package:只由小写字母组成、简短并包含一定的上下文信息、不要与标准库同名、不使用常用变量名作为包名、使用单数而不是复数、谨慎使用缩写。
控制流程
- 线性原理,处理逻辑尽量走直线,避免复杂的嵌套分支。
- 正常流程代码沿着屏幕向下移动,尽量保持正常代码路径为最小缩进。
- 提升代码可维护性和可读性。
- 故障问题大多出现在复杂的条件语句和循环语句中。
错误和异常处理
简单错误
即只出现一次的错误。优先使用errors.New来创建匿名变量来直接表示简单错误,需要格式化则使用fmt.Errorf。
错误的Wrap和Unwrap
错误的Wrap实际上是提供了一个error嵌套另一个error的能力,从而生成一个error的跟踪链。 在fmt.Errorf中使用%w关键字来将一个错误关联到错误链中。
错误判定
- 使用errors.ls判定一个错误是否为特定错误。
- 使用errors.As在错误链上获取特定种类的错误。
panic
- 不建议在业务代码中使用panic,建议使用error代替panic
- 当程序启动阶段发送不可逆转的错误时,可以在init或main函数中使用panic。
recover
- recover只能在被defer的函数中使用
- 嵌套无法生效
- 只在当前goroutine生效
- defer的语句是后进先出
总结
- error尽可能提供简明的上下文信息链,方便定位问题
- panic用于真正异常的情况
- recover生效范围,在当前goroutine的被defer的函数中生效
性能优化建议
- 性能表现需要实际数据衡量
- Go语言提供了支持基准性能测试的benchmark工具
slice预分配内存
尽可能在使用make()初始化切片时提供容量信息。
- 陷阱:大内存未释放 在已有切片基础上切片,不会创建新的底层数组。
- 解决:可使用copy替代re-slice
map预分配内存
不断向map中添加元素的操作会触发map的扩容,提前分配好空间可以减少内存拷贝和Rehash的消耗。建议根据实际需求提前预估好需要的空间。
字符串处理
- 使用strings.Builder:通过+拼接性能最差,strings.Builder,bytes.Buffer相近,strings.Buffer更快
使用空结构体节省内存
空结构体struct{}实例不占据任何的内存空间,可作为各种场景下的占位符使用。实现Set,可以考虑用map来代替。
automic包
- 锁是通过操作系统实现,属于系统调用
- automic操作是通过硬件实现,效率比锁高
- sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量
- 对于非数值操作,可以使用automic.Value,能承载一个interface{}
总结
- 避免常见的性能陷阱可以保证大部分程序的性能
- 普通应用代码,不要一味追求程序的性能
- 越高级的性能优化手段越容易出现问题
- 在满足正确可靠、简洁清晰的质量要求前提下提高程序性能
性能调优实战
简介
性能调优原则
- 依靠数据而不是猜测
- 定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
性能分析工具 pprof 实战
简介
分为 分析profile,工具tool,展示view 和 采样sample。
分析
- 网页
- 可视化终端
工具
- runtime/pprof
- net/http/pprof
展示
- Top
- 调用图-Graph
- 火焰图-FlameGraph
- Peek
- 源码-Source
- 反汇编-Disassemble
采样
- CPU
- 堆内存-Heap
- 协程-goroutine:goroutine泄露也会导致内存泄露。
- 锁-mutex
- 阻塞-block
- 线程创建-ThreadCreate
性能调优案例
简介
- 业务服务优化
- 基础库优化
- Go 语言优化
业务服务优化
- 服务:能单独部署,承载一定功能的程序
- 依赖:Service A 的功能实现依赖 Service B 的响应结构,称为 Service A 依赖 Service B
- 调用链路:能支持一个接口请求的相关服务集合及其相互之间的依赖关系
- 基础库:公共的工具包、中间件
- 流程:
- 建立服务性能评估手段
- 分析性能数据,定位性能瓶颈
- 重点优化项改造
- 优化效果验证
建立服务性能评估手段
服务性能评估方式(单独benchmark无法满足复杂逻辑分析,不同负载情况下性能表现差异)、请求流量构造、压测范围、性能数据采集
分析性能数据,定位性能瓶颈
- 使用库不规范
- 高并发场景优化不足
重点优化项改造
- 正确性是基础
- 响应数据diff:线上请求数据录制回放、新旧逻辑接口数据diff
优化效果验证
- 重复压测验证
- 上线评估优化效果:关注服务监控、逐步放量、收集性能数据
进一步优化,服务整体链路分析
- 规范上游服务调用接口,明确场景需求
- 分析链路,通过业务流程优化提升服务性能
基础库优化
AB实验SDK的优化
- 分析基础库核心逻辑和性能瓶颈:设计完善改造方案、数据按需获取、数据序列化协议优化
- 内部压测验证
- 推广业务服务落地验证
Go语言优化
编译器 & 运行时优化
- 优化内存分配策略
- 优化代码编译流程,生成更高效的程序
- 内部压测验证
- 推广业务服务落地验证
- 优点:接入简单,只需要调整编译配置;通用性强