1. 语言进阶

1.1 Goroutine

协程:用户态,栈MB级别

线程:内核态,栈kb级别

image.png

1.2 CSP(communicating sequential processes)

image.png

通过共享内存实现通信需要访问临界区的权限,影响性能

1.3 Channel(通过channel实现通信)

make(chan 元素类型,[缓冲大小])

无缓冲通道:即时同步

有缓冲通道:典型的生产者-消费者模型

Untitled

image.png

1.4 并发安全

1.5 waitGroup

go并发编程的计数器,开启协程+1,执行结束-1,主协程阻塞知道计数器为0

2. 依赖管理

2.1 Go 依赖管理的演进

GOPATH →Go Vendor→Go Module

  • 不同环境(项目)依赖的版本不同
  • 控制依赖库的版本

依赖管理三要素:

  • 配置文件,描述依赖(包如何唯一定位):go.mod
  • 中心仓库管理依赖库:Proxy
  • 本地工具:go get/mod

2.1.1 GOPATH弊端

image.png

  • 场景:A和B依赖于某一package的不同版本
  • 问题:无法实现package的多版本控制

2.1.2 Go Vendor弊端

image.png

  • 无法控制依赖版本
  • 更新项目又可能出现依赖冲突,导致编译出错

2.1.3 Go Module

终极目标:定义版本规则和管理项目依赖关系

  • 通过go.mod文件管理依赖包版本
  • 通过go get/go mod指令工具管理依赖包

2.3.1 依赖配置 - go.mod

image.png 单元依赖的依赖标识:[Module Path][Version/Pseudo-version]

2.3.2 依赖配置 - version

语义化版本:

MAJOR.{MAJOR}.MAJOR.{MINOR}.${PATCH}

V1.3.0

V2.3.0

基于commit伪版本:

vx.0.0-yyyymmddhhmmss-abcdefgh1234(伪版本号-时间戳-哈希校验码)

2.3.3 依赖配置 - indirect

image.png 会在间接依赖后面标识“indirect”

2.3.4 依赖配置 - incompatible

image.png

  • 主版本2+的模块会在模路径增加/vN后缀
  • 对于没有go.mod文件并且主版本2+的依赖,会+incompatible

2.3.4 依赖配置-依赖图

image.png

2.3.5 依赖分发-回源

  • 无法保证构建稳定性

    • 软件作者 增/删/改 软件版本
  • 无法保证依赖可用性

    • 同上
  • 增加第三方压力

    • GitHub顶不住

image.png

为了解决这个问题,提出了proxy

image.png

2.3.6 依赖分发-变量 GOPROXY

goproxy是服务站点url列表,“direct”表示源站(goproxy有点像缓存)

GOPROXY=“proxy1.cn, proxy2.cn, direct”

Proxy1→Proxy2→Direct

2.3.7 工具 - go get

  • update:默认
  • none:删除依赖
  • v1.1.2:tag版本,语义版本
  • 23dfd5:特定的commit
  • master:分支的最新commit

2.3.8 工具 - go mod

go mod:

  • init:初始化,创建go.mod文件
  • download:下载模块到本地缓存(把所有依赖给拉下来)
  • tidy:增加需要的依赖,删除不需要的依赖(提交代码前可以执行)
3. 测试

image.png

3.1 单元测试

image.png

3.1.1 单元测试-规则

func TestMain(m *testing.M) {
	//测试前:数据装载,配置初始化等前置工作
	code := m.Run()
	//测试后:释放资源等收尾工作
	os.Exit(code)
}
复制代码

3.1.4 单元测试-assert

import(
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestHelloTom(t *testing.T){
	output := HelloTom()
	expectOutput := "Tom"
	assert.Equal(t, expectOutput, output)
}

func HelloTom() string {
	return "Tom"
}
复制代码

3.1.5 单元测试-覆盖率

命令:

go test judgement_test.go judgment.go --cover
复制代码

Tips:

  • 一般覆盖率:50%~60%,较高覆盖率80%+
  • 测试分支相互独立,全面覆盖
  • 测试单元粒度足够小,函数单一职责

3.2 单元测试-依赖

单元测试的问题:

  • 函数会有外部依赖(DB,File,Cache),测试不稳定

但是单元测试的目标:

  • 幂等:单元测试重复运行,其结果相等
  • 稳定:单元测试相互隔离(任何时间,任何函数都能单独执行)

image.png

😇比如一个单元测试需要依赖一个外部的文件,如果这个文件被删除的话,单元测试就无法进行,所以引入了Mock机制

快速Mock函数:

  • 为一个函数打桩
  • 为一个方法打桩
//Patch replaces a function with another
func Patch(target, replacement interface{}) *PatchGuard {
	t := reflect.ValueOf(target)
	r := reflect.ValueOf(replacement)
	return &PatchGuard{t, r}
}

//Unpatch removes any monkey patches on target
//returns whether target was patched in the first place
func Unpatch(target interface{}) bool {
	return unpatchValue(reflect.ValueOf(target))
}
复制代码

比如我们有一个测试函数TestProcessFirstLine,它需要读取一个文件的首行,我们替换读取文件的代码为“写死的代码”

func TestProcessFirstLineWithMock(t *testing.T) {
	monkey.Patch(ReadFirstLine, func() string{return "line110"})
	defer monkey.Unpatch(ReadFirstLine)
	line := ProcessFirstline()
	assert.Equal(t, "line000", line)
}
复制代码