这是我参与第三届青训营-后端场笔记创作活动的第三篇笔记
编写高质量代码的规则
编程原则: 简单性(尽可能以简单清晰的逻辑编写代码), 可读性(更容易读懂的代码), 生产力(可以通过工具检验代码, 排除错误, 为项目进度提速)
编写高质量的Go代码: 代码格式, 注释, 命名规范, 控制流程, 错误和异常处理
注释应该写什么:
- 代码的作用 [函数的内容]
- 代码如何做 [解释某部分代码的实现原因]
- 代码如何实现的(即实现的原因) [解释某一行脱离上下文的代码, 解释其外部因素以及提供额外的上下文]
- 代码可能出错的情况 [运用场景, 解释代码的限制条件]
- 解释: 返回值
- 对于公共(public)提供的函数, 变量, 常量, 结构都得有注释
接口无需注释实现方法
命名规范:
- 简短明了
- 缩略词全大写,但处于开头时且无需作为导出或者public则使用小写
- 变量距离使用地方越远则需要携带更多上下文信息,例如全局变量
- 对外函数尽量使用富有信息量的参数名称
- 函数名尽量短且不携带包名的上下文
- 包名只使用小写字母,不用其他字符包括下划线,且能够包含一定的上下文信息,尽量缩写
编码规范:
- 尽量报错代码最小缩进(即减少if和for)
- erros.New来创建匿名变量来直接表示简单错误
- 格式化错误则用fmt.Errorf
- 利用errors.Is处理指定类型的错误
- erros.As可以获取特定类型的错误
- 不建议业务代码使用panic(但在启动阶段可以使用,尽早处理错误)
性能优化建议
优化的前提:程序正确,在此基础上依据测试数据再进行优化
benchmark 是 go自带的基准性能测试工具
slice初始化时先声明好大小
slice的陷阱: 在原有切片基础创建切片是不会释放原有数组,而且在其基础上创建小切片(利用copy代替创建切片操作)
map也是一样需要预分配
strings.Builder和bytes.Buffer由于其底层是[]byte数组,由于内存扩容策略无需每次拼接都分配新内存
builder.Grow(实质需要多少内存) -- 提前设置内存大小
原子操作的包atomic, 原子操作是靠硬件实现不同于锁(系统调用)
避免常见的性能陷阱其实就能提升较大的性能了
不要过度和过早优化,应该依照数据指标
性能分析工具pprof
使用方法:
go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
输入top:flat当前函数本身的执行耗时,flat%flat占CPU总时间的比例,sum%上面每一行flat%总和,cum指当前函数本身加上其调用函数的总耗时,cum% cum占CPU总时间的比例
图示化查看性能相关信息(heap改成其他的也可以,详细看http://localhost:6060/debug/pprof/ 即可) go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
Hep———SAMPLE:
- alloc_objects: 程序累计申请的对象数
- alloc_space: 程序累计申请的内存大小、
- inuse_objects: 程序当前持有的对象数
- inuse_space: 程序当前占用的内存大小
服务性能评估手段
-
服务性能评估方式
- 单独benchmark无法满足复杂逻辑分析
- 不同负载情况下性能表现差异
-
请求流量构造
- 不同请求参数覆盖逻辑不同
- 线上真实流量情况
-
压测范围
- 单机器压测
- 集群压测
-
性能数据采集
- 单机性能数据
- 集群性能数据
业务服务优化
上线后:重复压测验证,关注服务监控,逐步放量,收集性能数据
规范上游服务调用接口,明确场景需求(防止重复调用)
分析链路,通过业务流程优化提升服务性能
基础库优化
AB实验SDK的优化
- 分析基础库核心逻辑和性能瓶颈(设计完善改造方案,数据按需获取,数据序列化协议优化)
- 内部压测验证
- 推广业务服务落地验证
编译器与运行时优化
- 优化内存分配策略,编译流程
- 内部压测验证
- 推广业务服务落地验证