这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记,如有错误还请指正。 【有船启航】
上篇主要从编码规范和性能调优两方面入手。
高质量编程(简单性、可读性、生产力)
-
编码规范(5个方面)
-
(1)代码格式
- 使用 gofmt 自动格式化代码,保证所有的 Go 代码与官方推荐格式保持一致
- 提升可读性,风格一致的代码更容易维护、需要更少的学习成本、团队合作成本,同时可以降低 Review 成本
-
(2)注释(给出参考的golang中的注释)
-
(3)命名规范
-
变量
- 简洁胜于冗长
- 缩略词全大写,当缩略词位于开头且不作为公共变量时,全小写
- 变量距离其被使用的地方越远,越需要上下文提示信息
-
函数
- 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的
- 函数名尽量简短
- 当名为foo的包某个函数返同类型Foo(和包名一致)时,可省略类型信息而不导致歧义
- 当名为foo 的包某个函数返回类型T时(T并不是Foo),可以在函数名中加入类型信息
-
包(后三点尽量满足)
- 只由小写字母组成。不包含大写字母和下划线等字符
- 简短并包含一定的上下文信息。例如schema、task等
- 不要与标准库同名。例如不要使用sync或者strings
- 不使用常用变量名作为包名。例如使用bufio 而不是 buf
- 使用单数而不是复数。例如使用encoding而不是encodings
- 谨慎地使用缩写。例如使用fmt在不破坏上下文的情况下比 format更加简短
-
-
(4)控制流程
- 避免嵌套,保持正常流程清晰 比如if-else的语句都有return,那将正常返回语句放在原else块后,取消冗余的else。这样的if类似于一个筛选,而后再添加其他流程也很方便的写在if语句之外。
- 尽量保持正常代码路径为最小缩进
-
(5)错误和异常处理
-
panic 用于真正异常的情况
-
error 尽可能提供简明的上下文信息,方便定位问题
-
recover 生效范围,在当前 goroutine 的被 defer 的函数中生效
- 错误判定:
errors.As()可以把导致错误的命令取出 - panic:不建议在业务代码中使用 panic,如果当前 goroutine 中所有 deferred 函数都不包含 recover 就会造成整个程序崩溃,当程序启动阶段发生不可逆转的错误时,可以在 init 或 main 函数中使用 panic
- recover 只能在被 defer 的函数中使用,嵌套无法生效,只在当前 goroutine 生效,如果需要更多的上下文信息,可以 recover 后在 log 中记录当前的调用栈。
-
-
-
性能优化建议
- Benchmark
- 进行基准测试的文件必须以*_test.go的文件为结尾,这个和测试文件的名称后缀是一样的,例如abc_test.go
- 参与Benchmark基准性能测试的方法必须以Benchmark为前缀,例如BenchmarkABC()
- 参与基准测试函数必须接受一个指向Benchmark类型的指针作为唯一参数,*testing.B
- 性能测试命令为go test [参数],如
go test -bench=. -benchmem
- Slice
- slice预分配内存:尽可能在make()初始化切片时提供容量信息
- 由于创建一个新的切片会复用原来切片的底层数组,那么当原切片较大,代码在原切片基础上新建小切片原底层数组在内存中有引用,得不到释放,这时可使用
copy替代re-slice(创建切片)
- Map
- map预分配内存:尽可能在make()初始化切片时提供容量信息
- 字符串处理
- 使用strings.Builder拼接字符串
- 空结构体
- 使用空结构体节省内存 空结构体struct{}实例不占据任何内存空间 可作为各种场景下的占位符使用:节省资源;空结构体本身具有很强的语义,即不需要任何值,仅作为占位符。(例 golang-set在线程安全的实现)
- atomic包
- 比加锁性能要好(锁的实现是通过操作系统来实现的,属于系统调用,调用成本高;atomic操作时通过硬件实现效率比锁高)
- sync.Mutex 应用来保护一段逻辑,保护一个变量用atomic包更合适
- 对于非数值操作,使用atomic.Value,能承载(放进去)一个interface{}
- Benchmark
性能调优实战
-
性能调优原则
- 依靠数据而非猜测,统一标准的测试
- 定位最大瓶颈,解决主要矛盾
- 不要过早优化,不要过度优化
-
性能分析工具pprof(类似于Java的VisualVM)
- 说明:分析应用在CPU、Memory上的耗费
- CPU: go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10":
- top 查看排名 .
- list 具体func的执行代码 .
- web 调用关系可视化 .
- Heap-堆内存:go tool pprof -http=:8080"http://loaclhost:6060/debug/pprof/heap" :
- alloc_objects:程序累计申请的对象数.
- alloc_space:程序累计申请的内存大小.
- inuse_objects:程序当前持有的对象数.
- inuse_space:程序当前持有的内存大小.
- goroutine-协程:go tool pprof -http=:8080"http://localhost:6060/debug/pprof/goroutine":
- 在火焰图FlameGraph中找到函数后,在source源码视图下搜索
- mutex-锁:go tool pprof -http=:8080"http://localhost:6060/debug/pprof/mutex":
- 先从graph调用图中找有问题的函数,再去source源码视图下搜索定位
- block-阻塞:go tool pprof -http=:8080"http://localhost:6060/debug/pprof/block":
- 同上,先从graph调用图中找有问题的函数,再去source源码视图下搜索定位
-
性能调优案例
- 业务服务优化 分析节点 分析链路
- 基础库优化(监控打点、日志库等) 分析核心逻辑和性能瓶颈 压测验证
- Go语言优化(编译器、运行时分配策略) 优化内存分配策略 优化代码编译流程,生成更高效的程序 压测验证