很多的人都只在乎自己核心技术的提高和所谓的上层团队协作,却忽视了团队合作中最重要的一点——代码可读性。

三大标准:

  • 简单性:减少冗余逻辑
  • 可读性:良好的注释、良好的函数
  • 生产力

一句话:让后来人和以后的自己能迅速读懂 :)

规范

规范工具

  • 用gofmt、goimports来规范代码的format

注释

  1. 公共符号,如函数、结构体、变量都要添加注释。
  2. 不明显不简短的公共功能要加注释
  3. 库中的任何函数基本都要加注释
  4. 实现接口的方法可不需要注释
  5. Good code has lots of comments, bad code requires lots of comments.
  6. 函数注释可包含:input、output、description、(author、time)
  7. 可提供额外上下文(特别是在if、switch、for中)
  8. 可提供限制条件、边界条件、抛出报错信息

变量

  1. 简洁
  2. 缩略词全大写(这一点gorm的gen做得很不好:\)
  3. 无歧义(t和deadline)

函数名

  1. 不携带包名中有的信息
  2. 简洁

包名

  1. 小写
  2. 不使用特殊关键词作为包名

控制流程

  1. 避免复杂的嵌套分支。

这点很重要。。。亲身经历,自己以前写过复杂的逻辑,最长缩进都有36格了。

看看我的bad code :)(QQChannelChatGPT/model/provider/provider_openai_official.py at master · Soulter/QQChannelChatGPT (github.com)

错误处理

Error Wrap是一种用于在错误信息中添加上下文信息的机制,以便更好地理解和追踪错误发生的原因。Error Wrap是由Go 1.13版本引入的,并通过在errors包中添加了**UnwrapWrap**函数来支持。

Error Wrap的主要目的是在不丢失原始错误信息的情况下,将额外的上下文信息包含在错误链中。这样做的好处是,当发生错误时,我们可以快速地追溯错误的来源,并了解到错误传播的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"errors"
"fmt"
)

func main() {
err := errors.New("原始错误")
wrappedErr := fmt.Errorf("额外的上下文信息:%w", err)

fmt.Println(wrappedErr)
}

Go 1.13版本之后,Go语言引入了一个新的错误包装机制,使用**errors包的Wrap函数和Wrapf**函数可以更方便地进行错误包装,它们可以创建一个包含上下文信息的错误类型。

在错误链下,可以使用error.Is和error.As来判定错误类型,对特定错误进行特殊的处理。

  • 不建议在业务代码中使用panic(除init和main函数)
  • 可使用recover(只在当前goroutine中生效)

优化

建议

评估性能可以用上面提到的benchmark

  1. 【BIG】slice应当预分配内存,尽可能在使用make()初始化切片的时候提供容量信息,减少扩容操作(slice底层数组满了之后会执行cap扩容,会分配新内存,并且copy数组。扩容不是简单的2x。)
  2. 【BIG】上面建议的陷阱,如果size特别大的时候,要留意!在此切片上如果有临时切片引用的话,由于GC的引用计数机制,在不想使用这个slice之后,会造成memory leak。解决方案:自己copy
  3. 【BIG】字符串在多次拼接操作的时候,切记使用StringBudder或者bytes.Buffer (前者更快,底层数组是相同的[]byte,但扩容策略不一样。)

  1. 【GOOD】占位符使用struct{}。不占用任何空间。可以用于不需要value的map:map[string]struct{}。(用bool的话也会多占据n*1B的内存。)
  2. 【GOOD】保护变量线程安全使用atomic包。比mutex快。锁的实现是通过OS实现,属于system call;atomic是通过硬件实现,比mutex效率高;非数值操作可以用atomic.Value,可以维护一个interface{}!

优化工具——pprof

一个功能非常强大的golang程序性能分析工具。

指令:

启动

1
2
3
(CPU)go tool pprof "https://localhost:6060/debug/pprof/profile?seconds=10"
(HEAP)go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
...

top

flat==cum时:没调用另一个函数的函数

flat==0:函数只被其他函数调用

list

太强大了!

web——可视化

可以可视化查看函数之间的调用关系和占用情况。

采样过程和原理:

  1. 每10ms由os向进程发送SIGPROC记录堆栈信息。
  2. 每100ms读取记录的调用栈并写入缓冲流。

服务性能评估

QPS是”Queries Per Second”(每秒查询数)的缩写,用于表示系统在一秒钟内能够处理的查询或请求的数量。QPS是衡量系统性能的重要指标,通常用于评估服务器、数据库或网络设备的处理能力。

  1. 真实数据压测(单机器、集群)、灰度测试
  2. 性能数据采集(单机、集群)
  3. 单个逻辑优化
  4. 服务整体链路分析(多个服务之间调用链路是否可以加cache?service合并?重复调用?)
  5. 结合pprof的各种数据(如flamegraph)
  6. 结合Ediff——检测性能优化后,请求功能的结果与优化前的差别。
  7. AB测试
  8. 决策更新依赖库版本、SDK版本以获得上游优化。