Go上手笔记(二) 优化上篇| 青训营笔记

227 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记,如有错误还请指正。 【有船启航】

上篇主要从编码规范和性能调优两方面入手。

高质量编程(简单性、可读性、生产力)

  1. 编码规范(5个方面)

    • (1)代码格式

      • 使用 gofmt 自动格式化代码,保证所有的 Go 代码与官方推荐格式保持一致
      • 提升可读性,风格一致的代码更容易维护、需要更少的学习成本、团队合作成本,同时可以降低 Review 成本
    • (2)注释(给出参考的golang中的注释)

    • (3)命名规范

      1. 变量

        • 简洁胜于冗长
        • 缩略词全大写,当缩略词位于开头且不作为公共变量时,全小写
        • 变量距离其被使用的地方越远,越需要上下文提示信息
      2. 函数

        • 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的
        • 函数名尽量简短
        • 当名为foo的包某个函数返同类型Foo(和包名一致)时,可省略类型信息而不导致歧义
        • 当名为foo 的包某个函数返回类型T时(T并不是Foo),可以在函数名中加入类型信息
      3. 包(后三点尽量满足)

        • 只由小写字母组成。不包含大写字母和下划线等字符
        • 简短并包含一定的上下文信息。例如schema、task等
        • 不要与标准库同名。例如不要使用sync或者strings
        • 不使用常用变量名作为包名。例如使用bufio 而不是 buf
        • 使用单数而不是复数。例如使用encoding而不是encodings
        • 谨慎地使用缩写。例如使用fmt在不破坏上下文的情况下比 format更加简短
    • (4)控制流程

      • 避免嵌套,保持正常流程清晰 比如if-else的语句都有return,那将正常返回语句放在原else块后,取消冗余的else。这样的if类似于一个筛选,而后再添加其他流程也很方便的写在if语句之外。
      • 尽量保持正常代码路径为最小缩进
    • (5)错误和异常处理

      • panic 用于真正异常的情况

      • error 尽可能提供简明的上下文信息,方便定位问题

      • recover 生效范围,在当前 goroutine 的被 defer 的函数中生效

      1. 错误判定:errors.As()可以把导致错误的命令取出
      2. panic:不建议在业务代码中使用 panic,如果当前 goroutine 中所有 deferred 函数都不包含 recover 就会造成整个程序崩溃,当程序启动阶段发生不可逆转的错误时,可以在 init 或 main 函数中使用 panic
      3. recover 只能在被 defer 的函数中使用,嵌套无法生效,只在当前 goroutine 生效,如果需要更多的上下文信息,可以 recover 后在 log 中记录当前的调用栈。
  2. 性能优化建议

    1. Benchmark
      • 进行基准测试的文件必须以*_test.go的文件为结尾,这个和测试文件的名称后缀是一样的,例如abc_test.go
      • 参与Benchmark基准性能测试的方法必须以Benchmark为前缀,例如BenchmarkABC()
      • 参与基准测试函数必须接受一个指向Benchmark类型的指针作为唯一参数,*testing.B
      • 性能测试命令为go test [参数],如 go test -bench=. -benchmem
    2. Slice
      • slice预分配内存:尽可能在make()初始化切片时提供容量信息
      • 由于创建一个新的切片会复用原来切片的底层数组,那么当原切片较大,代码在原切片基础上新建小切片原底层数组在内存中有引用,得不到释放,这时可使用copy替代 re-slice(创建切片)
    3. Map
      • map预分配内存:尽可能在make()初始化切片时提供容量信息
    4. 字符串处理
      • 使用strings.Builder拼接字符串
    5. 空结构体
      • 使用空结构体节省内存 空结构体struct{}实例不占据任何内存空间 可作为各种场景下的占位符使用:节省资源;空结构体本身具有很强的语义,即不需要任何值,仅作为占位符。(例 golang-set在线程安全的实现)
    6. atomic包
      • 比加锁性能要好(锁的实现是通过操作系统来实现的,属于系统调用,调用成本高;atomic操作时通过硬件实现效率比锁高)
      • sync.Mutex 应用来保护一段逻辑,保护一个变量用atomic包更合适
      • 对于非数值操作,使用atomic.Value,能承载(放进去)一个interface{}

性能调优实战

  1. 性能调优原则

    • 依靠数据而非猜测,统一标准的测试
    • 定位最大瓶颈,解决主要矛盾
    • 不要过早优化,不要过度优化
  2. 性能分析工具pprof(类似于Java的VisualVM)

  3. 性能调优案例

    • 业务服务优化 分析节点 分析链路
    • 基础库优化(监控打点、日志库等) 分析核心逻辑和性能瓶颈 压测验证
    • Go语言优化(编译器、运行时分配策略) 优化内存分配策略 优化代码编译流程,生成更高效的程序 压测验证