很多的人都只在乎自己核心技术的提高和所谓的上层团队协作,却忽视了团队合作中最重要的一点——代码可读性。
三大标准:
- 简单性:减少冗余逻辑
- 可读性:良好的注释、良好的函数
- 生产力
一句话:让后来人和以后的自己能迅速读懂 :)
规范
规范工具
- 用gofmt、goimports来规范代码的format
注释
- 公共符号,如函数、结构体、变量都要添加注释。
- 不明显不简短的公共功能要加注释
- 库中的任何函数基本都要加注释
- 实现接口的方法可不需要注释
- Good code has lots of comments, bad code requires lots of comments.
- 函数注释可包含:input、output、description、(author、time)
- 可提供额外上下文(特别是在if、switch、for中)
- 可提供限制条件、边界条件、抛出报错信息
变量
- 简洁
- 缩略词全大写(这一点gorm的gen做得很不好:\)
- 无歧义(t和deadline)
函数名
- 不携带包名中有的信息
- 简洁
包名
- 小写
- 不使用特殊关键词作为包名
控制流程
- 避免复杂的嵌套分支。
这点很重要。。。亲身经历,自己以前写过复杂的逻辑,最长缩进都有36格了。
看看我的bad code :)(QQChannelChatGPT/model/provider/provider_openai_official.py at master · Soulter/QQChannelChatGPT (github.com))
错误处理
Error Wrap是一种用于在错误信息中添加上下文信息的机制,以便更好地理解和追踪错误发生的原因。Error Wrap是由Go 1.13版本引入的,并通过在errors包中添加了**Unwrap
和Wrap
**函数来支持。
Error Wrap的主要目的是在不丢失原始错误信息的情况下,将额外的上下文信息包含在错误链中。这样做的好处是,当发生错误时,我们可以快速地追溯错误的来源,并了解到错误传播的路径。
1 | package main |
Go 1.13版本之后,Go语言引入了一个新的错误包装机制,使用**errors
包的Wrap
函数和Wrapf
**函数可以更方便地进行错误包装,它们可以创建一个包含上下文信息的错误类型。
在错误链下,可以使用error.Is和error.As来判定错误类型,对特定错误进行特殊的处理。
- 不建议在业务代码中使用panic(除init和main函数)
- 可使用recover(只在当前goroutine中生效)
优化
建议
评估性能可以用上面提到的benchmark
- 【BIG】slice应当预分配内存,尽可能在使用make()初始化切片的时候提供容量信息,减少扩容操作(slice底层数组满了之后会执行cap扩容,会分配新内存,并且copy数组。扩容不是简单的2x。)
- 【BIG】上面建议的陷阱,如果size特别大的时候,要留意!在此切片上如果有临时切片引用的话,由于GC的引用计数机制,在不想使用这个slice之后,会造成memory leak。解决方案:自己copy
- 【BIG】字符串在多次拼接操作的时候,切记使用StringBudder或者bytes.Buffer (前者更快,底层数组是相同的[]byte,但扩容策略不一样。)
- 【GOOD】占位符使用struct{}。不占用任何空间。可以用于不需要value的map:map[string]struct{}。(用bool的话也会多占据n*1B的内存。)
- 【GOOD】保护变量线程安全使用atomic包。比mutex快。锁的实现是通过OS实现,属于system call;atomic是通过硬件实现,比mutex效率高;非数值操作可以用atomic.Value,可以维护一个interface{}!
优化工具——pprof
一个功能非常强大的golang程序性能分析工具。
指令:
启动
1 | (CPU)go tool pprof "https://localhost:6060/debug/pprof/profile?seconds=10" |
top
flat==cum时:没调用另一个函数的函数
flat==0:函数只被其他函数调用
list
太强大了!
web——可视化
可以可视化查看函数之间的调用关系和占用情况。
采样过程和原理:
- 每10ms由os向进程发送SIGPROC记录堆栈信息。
- 每100ms读取记录的调用栈并写入缓冲流。
服务性能评估
QPS是”Queries Per Second”(每秒查询数)的缩写,用于表示系统在一秒钟内能够处理的查询或请求的数量。QPS是衡量系统性能的重要指标,通常用于评估服务器、数据库或网络设备的处理能力。
- 真实数据压测(单机器、集群)、灰度测试
- 性能数据采集(单机、集群)
- 单个逻辑优化
- 服务整体链路分析(多个服务之间调用链路是否可以加cache?service合并?重复调用?)
- 结合pprof的各种数据(如flamegraph)
- 结合Ediff——检测性能优化后,请求功能的结果与优化前的差别。
- AB测试
- 决策更新依赖库版本、SDK版本以获得上游优化。