这是我参与「第五届青训营」伴学笔记创作活动的第 3 天
高质量编程
编码规范
-
代码格式:推荐使用 gofmt 和 goimports 工具自动格式化代码。对于 GoLand,可以在
Settings - Tools - Actions on Save中打开Reformat code和Optimize imports
,也可通过 Ctrl+Alt+L 快捷键格式化代码。
-
注释:解释代码作用、解释代码如何做的、解释代码实现的原因、解释代码什么情况会出错;公共符号始终要注释
-
命名规范:变量、函数、包
-
控制流程:避免嵌套,保持正常流程清晰;尽量保持正常代码路径为最小缩进
// Bad if foo { return x } else { return nil } // Good if foo { return x } return nil
-
错误和异常处理:
-
对于简单错误(仅出现一次,在其他地方不需要捕获),优先使用
errors.New创建匿名变量直接表示;如有格式化需求,使用fmt.Errorf;// 一个例子 func defaultCheckRedirect(req *Request, via []*Request) error { if len(via) >= 10 { // 使用errors.New return errors.New("stopped after 10 redirects.") } return nil // 去掉不必要的else }
-
在
fmt.Errorf中使用%w将一个错误关联至错误链中;// 一个例子 list, _, err := c.GetBytes(cache.Subkey(a.actionID, "srcfiles")) if err != nil { return fmt.Errorf("reading srcfiles list: %w", err) }
-
使用
errors.Is判定一个错误为特性错误,比起直接使用==的好处是可以判断错误链上的所有错误是否含有特定错误;// 一个例子 data, err = lockedfile.Read(targ) if errors.Is(err, fs.ErrNotExist) { return []byte{}, nil } return data, err
-
使用
errors.As获取错误链上特定种类的错误;// 一个例子 if _, err := os.Open("non-existing"); err != nil { var pathError *fs.PathError if errors.As(err, &pathError) { fmt.Println("Failed at path:", pathError.Path) } else { fmt.Println(err) } }
-
只有在程序启动阶段发生不可逆转的错误时才使用
panic()(类似于 Java中java.lang.Error的地位,但是 Go 可以使用revover()语句来从panic中恢复;
性能优化建议
- Benchmark:支持基准性能测试的工具
- Slice, Map预分配内存:
- 大内存未释放陷阱:注意为切片创建切片不会创建新的底层数组,这可能会导致内存泄漏发生,此时可用
copy代替 re-slice。 - strings.Builder:在字符串拼接的过程中,使用strings.Builder往往比直接
+要快。这和 Java 倒是十分相似,Java 也推荐使用StringBuilder拼接多个字符串;其实他们的底层逻辑都是类似的。 - 空结构体:空结构体
struct{}实例不占据任何的内存空间,利用map+struct{}创建set。 - atomic包:cas乐观锁,使用
atomic包代替锁修改变量。
性能调优实战
性能调优分析工具pprof
- 采样数据说明:
- allocs:内存分配情况
- blocks:阻塞操作情况
- cmdline:程序启动命令及
- goroutine:当前所有goroutine的堆栈信息
- heap:堆上内存使用情况(同alloc)
- mutex:锁竞争操作情况
- profile: CPU占用情况
- threadcreate:当前所有创建的系统线程的堆栈信息
- trace:程序运行跟踪信息
- 常用指令:
-
输入
top可以查看CPU占用最高的函数,参数说明: -
如果只需要查看最高的N个函数,则输入
topN即可 -
命令
list根据指定的正则表达式查找代码行,例如list Eat -
命令
web调用关系可视化,生成一张调用关系图,会默认使用浏览器打开,非常直观
性能调优案例
- 业务服务优化
- 基础库优化
- Go语言优化