Go语言编码规范、性能优化建议|青训营笔记

190 阅读4分钟

这是我参与「第五届青训营」伴学笔记创作活动的第3天

编码规范

注释

  • 公共符号、函数始终要注释

包中声明的每一个公共符号、变量、常量、函数以及结构体都需要注释

任何不明显也不简短的公共功能也需要给予注释

无论长度或复杂度如何,对库中的任何函数都必须进行注释

有一例外,不需要注释实现接口的方法

  • 注释应该有的作用

应该解释代码作用

应该解释代码如何做的

应该解释代码实现的原因

应该解释代码为什么会出错

  • 注释应该提供未表达出来的上下文信息

代码格式

推荐使用go fmt自动格式化代码

go imports:在go fmt的基础上加上了依赖包管理,自动增加删除依赖包应用、将依赖包按字母排序并分类

命名规范

变量命名

  • 简洁胜于冗长
  • 缩略词全部大写,但当位于变量头部并且不需要导出的时候,全部小写,比如使用ServerHTTP而不是ServerHttp
  • 变量距离被使用的地方越远,则需要携带越多的上下文信息

函数命名

  • 函数名不携带包的上下文信息,因为总是成对出现
  • 函数名尽可能简短
  • 当名为foo的包中的某个函数返回类型foo时,可以省略类型信息而不导致歧义,但返回类型为T时,则需要在函数名中加入类型信息

控制流程

  • 避免嵌套,保持正常流程清晰
  • 尽量保持正常代码路径为最小缩进
    • 优先处理错误情况/特殊情况,尽早返回
  • 处理逻辑尽量走直线
  • 故障问题大多数出现在循环语句以及复杂的判断语句中

错误与异常处理

简单错误

  • 简单错误是指只会出现一次的错误,并且在其他地方不需要捕获该错误
  • 优先使用errors.New来创建匿名变量直接表示简单错误
  • 如果有格式化的要求,使用fmt.Errorf

错误的Wrap和Unwarp

  • 错误的Wrap实际上是提供一个error嵌套另一个error的能力,从而能够跟踪错误链
  • 在fmt.Errorf中使用%w将一个错误关联到错误链中

错误判定

  • 判定一个错误是不是特定错误,使用errors.ls
  • 不同于使用==,使用该方法可以判定错误链上的所有错误是否含有特定错误
  • 在错误链上获取特定错误内容使用errors.As

panic

  • 议在业务中使用panic
  • 调用函数不包含recovery会造成程序崩溃
  • 若问题可以被屏蔽或者解决,建议使用error代替panic
  • 序启动的初始阶段出现不可逆转的错误时,可以在init或者main函数中使用panic

recover

  • cover只能在被defer的函数中使用
  • 嵌套无法生效
  • 只能在当前的协程中使用
  • defer语句是后进先出
  • 如果需要更多的上下文信息,可以在recover后log中记录当前的调用栈

性能优化建议

  • 性能表现需要数据说明

  • go中提供了支持基准性能测试的benchmark工具,使用命令:go test -bench=. -benchmem

39cb1d89f125b3f9f4f4bd7fc2d4b83.jpg

slice预分配内存

尽可能使用make()初始化切片的时候提供容量信息

  • 切片本质上是一个数组片段的描述
    • 包括数组指针
    • 片段的长度
    • 片段的容量
  • 切片操作并不复制切片指向的元素
  • 创建一个新的切片会复用原来切片的底层数组

大内存没有释放陷阱

在已有切片的基础上创建切片不会创建新的切片数组

原切片较大,代码在原切片的基础上新建小切片,底层数组在内存中有引用,得不到释放,因此我们可以使用copy代替re-slice

map预分配内存

  • 不断向map中添加元素会触发map扩容
  • 提前分配好空间可以减少内存拷贝和Rehash的消耗
  • 建议根据实际情况提前预估好分配的空间

strings.Builder

使用strings.Builder拼接字符串会减少性能消耗,因为string类型是不可变的,每次使用+都会重新分配内存

使用空结构体节省内存

空结构体并不会占据任何的内存空间,可以作为占位符使用,并且可以节省资源

在map中使用这个特点,可以用来实现set,因为只需要key不需要value,会更加节省内存

使用atomic包

使用atomic包可以提高性能