「Go 语言工程实践」学习笔记 | 青训营

109 阅读3分钟

学习笔记第一篇。总共第一篇。

一、本堂课重点内容:

「Go 语言工程实践」字节跳动青训营 - 后端专场

  • 本堂课主要介绍了Go语言的进阶知识,Go依赖管理的演进、Go module管理依赖用法、测试、项目实战。

二、详细知识点介绍:

Golang依赖管理的演进

GO PATH -> GO VENDOR -> GO MODULE

GO PATH弊端:无法实现package的多版本控制。不同的项目A和B可能依赖某package的不同版本。

GO VENDOR:项目下增加vendor文件夹,先找vendor再找gopath。

GO VENDOR弊端:某个项目A可能依赖项目B和项目C,但项目B和C可能又依赖某一项目D的不同版本,项目A的控制住无法很好地控制项目B和项目C,当项目A去更新它的依赖B和C时,可能造成更底层的依赖D冲突。

Go MODULE:通过go.mod管理依赖。通过go get/go mod管理。Go1.16后默认开启。

依赖配置 - version

  1. 语义化版本v${MARJOR}.$MINOR$.PATCH$$
  2. 伪版本vx.0.0.yymmddhhmmss-commitid

go.mod中主版本2+需要加后缀/vN 对于没有go.mod并且主版本2+的依赖,要添加+incompatible

问题:如果X项目依赖了A、B两个项目,且A、B分别依赖了C项目的v1.3、v1.4两个版本,最终编译时所使用的C项目的版本为如下哪个选项?(单选)

A. v1.3

B. v1.4

C.A用到C时用v1.3编译,B用到C时用v1.4编译

答案:B。因为根据语义化版本,v1.3与v1.4同属于v1 major version,故v1.4应该兼容v1.3。

依赖分发 - GOPROXY

使用PROXY可以避免构建稳定性(防止源修改/删除版本)和依赖可用性(防止源删除软件)

GOPROXY="proxy1.cn,https://proxy2.cn,…" direct表示源站

go get(Module-Aware模式,不是GOPATH模式下的go get)

go get example.org/pkg

常用后缀:

@update 拉取最新版本(会更新之前已下载过的库到最新的Minor版本)
@none 删除依赖
@v1.1.2 拉取特定语义化版本
@23dfdd5 拉取特定commit版本
@master 拉取特定分支的最新版本

go mod

go mod init 初始化并创建go.mod go mod download 下载模块到本地缓存 go mod tidy 增加需要的依赖并删除不需要的依赖

回归测试、集成测试、单元测试

单元测试

  • 测试文件以_test.go结尾
  • 测试函数func TestXXX(t *testing.T)
  • 测试初始化逻辑TestMain
func TestMain(m *testing.M) {
  // init
  m.Run()
  // free
  os.Exit(code)
}

一个assert包:github.com/stretchr/testify/assert stretchr/testify: A toolkit with common assertions and mocks that plays nicely with the standard library (github.com)

代码覆盖率:go test xxx.go --cover

一般覆盖率50~60%,较高覆盖率80%+

  • 保证测试分支相互独立、全面覆盖
  • 测试单元粒度足够小 ,单一职责。

mock

有时程序需要外部依赖DB、File、Cache,为了避免这些外部状态的变化对测试造成影响,需要mock来达成幂等 & 稳定

一个mock包: github.com/bouk/monkey

monkey.Patch(fff, func(){
    /// ..
})
defer monkey.Unpatch(fff)
// our test code using function fff

基准测试

测试函数

func TestXXX(b *testing.B) {
  init()
  b.ResetTimer()
  for i:=0; i<b.N; i++ {
      CallToOurFunc()
  }
}

如果有初始化逻辑不需要计入测试时间,可以在初始化后调用b.ResetTimer()

并行调用的压力测试

func TestXXX(b *testing.B) {
  init()
  b.ResetTimer()
  b.RunParellel(func(pb *testing.PB){
      for pb.Next(){ // pb.Next() 返回是否还需要更多调用
          CallToOurFunc()
      }
  })
}

字节跳动的go工具库:bytedance/gopkg: Universal Utilities for Go (github.com)

bytedance/gopkg/lang/fastrand在并行中比rand表现更好,但不能设置seed。

三、实践练习例子:

  • 数据层model
  • 业务层entity
  • 视图层view