1.1 Goroutine
协程:用户态,栈MB级别
线程:内核态,栈kb级别
1.2 CSP(communicating sequential processes)
通过共享内存实现通信需要访问临界区的权限,影响性能
1.3 Channel(通过channel实现通信)
make(chan 元素类型,[缓冲大小])
无缓冲通道:即时同步
有缓冲通道:典型的生产者-消费者模型
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弊端
- 场景:A和B依赖于某一package的不同版本
- 问题:无法实现package的多版本控制
2.1.2 Go Vendor弊端
- 无法控制依赖版本
- 更新项目又可能出现依赖冲突,导致编译出错
2.1.3 Go Module
终极目标:定义版本规则和管理项目依赖关系
- 通过go.mod文件管理依赖包版本
- 通过go get/go mod指令工具管理依赖包
2.3.1 依赖配置 - go.mod
单元依赖的依赖标识:[Module Path][Version/Pseudo-version]
2.3.2 依赖配置 - version
语义化版本:
MAJOR.{MINOR}.${PATCH}
V1.3.0
V2.3.0
基于commit伪版本:
vx.0.0-yyyymmddhhmmss-abcdefgh1234(伪版本号-时间戳-哈希校验码)
2.3.3 依赖配置 - indirect
会在间接依赖后面标识“indirect”
2.3.4 依赖配置 - incompatible
- 主版本2+的模块会在模路径增加/vN后缀
- 对于没有go.mod文件并且主版本2+的依赖,会+incompatible
2.3.4 依赖配置-依赖图
2.3.5 依赖分发-回源
-
无法保证构建稳定性
- 软件作者 增/删/改 软件版本
-
无法保证依赖可用性
- 同上
-
增加第三方压力
- GitHub顶不住
为了解决这个问题,提出了proxy
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.1 单元测试
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),测试不稳定
但是单元测试的目标:
- 幂等:单元测试重复运行,其结果相等
- 稳定:单元测试相互隔离(任何时间,任何函数都能单独执行)
😇比如一个单元测试需要依赖一个外部的文件,如果这个文件被删除的话,单元测试就无法进行,所以引入了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)
}
复制代码