这是我参与「第五届青训营」笔记创作活动的第2天。
一、本堂课重点知识
今天学习的主要内容是语言进阶、依赖管理、单元测试以及项目实战。
二、详细知识点介绍
1. 并发 VS 并行
这篇文章有很好的解释。
1.1 Goroutine
- 协程:用户态,轻量级线程,栈KB级别。
- 线程:内核态,线程跑多个协程,栈MB级别。
详细可以参考Golang协程和线程区别 - yuhaohao - 博客园 (cnblogs.com)
1.2 CSP (Communicating Sequential Processes)
更提倡通过通信共享内存而不是通过共享内存实现通信。 后者需要使用互斥量加锁,在一定程度上影响程序的性能。
1.3 Channel
make(chan 元素类型,[缓冲大小])
- 无缓冲通道 make(chan int)
- 有缓冲通道 make(chan int,2)
1.4 并发安全 Lock
不加锁限制会影响安全。
1.5 WaitGroup
计数器:开启协程+1;执行结束-1;朱携程阻塞1知道计数器为0。
- func (wg *WaitGroup) Add(delta int)
- func (wg *WaitGroup) Done()
- func (wg *WaitGroup) Wait()
2 依赖管理
2.1 背景
- 工程项目不可能基于标准库0~1编码搭建
- 管理依赖库
2.2 Go 依赖管理演进
GOPATH Go Vendor Go Module
- 不同环境(项目)依赖的版本不同
- 控制依赖库的版本
2.2.1 GOPATH
- 环境变量 $GOPATH
- bin 项目编译的二进制文件
- pkg 项目编译的中间产物,加速编译
- src 项目源码
- 项目代码直接依赖src下的代码
- go get下载最新版本的包到src目录下
- 弊端
- 场景:A和B依赖于某一package的不同版本。
- 问题:无法实现package的多版本控制。
2.2.2 Go Vendor
通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。
- 项目目录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor
- 依赖寻址方式:vendor GOPATH
- 弊端
- 场景:同属Project A的Package B和Package C同时依赖于同一个Package D的不同版本。
- 问题:
- 无法控制依赖的版本。
- 更新项目又可能出现依赖冲突,导致编译出错。
2.2.3 Go Module
- 通过go.mod文件管理依赖包版本
- 通过go get/go mod指令工具管理依赖包
终极目标:定义版本规则和管理项目依赖关系
2.3 依赖管理三要素
- 配置文件,描述依赖 go.mod
- 中心仓库管理依赖库 Proxy
- 本地工具 go get/mod
2.3.1 依赖配置-go.mod
由依赖管理基本单元、原生库、单元依赖三部分组成。
2.3.2 依赖配置-version
- 语义化版本
- ${MAJOR}.${MINOR}.${PATCH}
- V1.3.0 V2.3.0
- 基于commit伪版本
- vx.0.0-yymmddhhmmss-12位哈希校验码
2.3.3 依赖配置-indirect
A B C
- A B 直接依赖
- A C 间接依赖
2.3.4 依赖配置-incompatible
- 主版本2+模块会在模块路径增加/vN后缀。
- 对于没有go.mod文件并且主版本2+的依赖,会+incompatible
2.3.5 依赖配置-Proxy
2.3.6 依赖分发-变量GOPROXY
2.3.7 工具-go get
go get example.org/pkg
- @update 默认
- @none 删除依赖
- @v1.1.2 tag版本,语义版本
- @23dfdd5 特定的commit
- @master 分支的最新commit
2.3.8 工具-go mod
go mod
- init 初始化,创建go.mod文件
- download 下载模块到本地缓存
- tidy 增加需要的依赖,删除不需要的依赖
3 测试
回归测试 集成测试 单元测试 从左至右,覆盖率逐层变大,成本却逐层降低。
3.1 单元测试
对期望与输入经由测试单元得出的输出进行校对,可以保证质量以及提升效率。
3.1.1 单元测试-规则
- 所有测试文件以_test.go结尾
func TestXxx(*testing.T)- 初始化逻辑放到TestMain中
3.1.2 单元测试-运行
go test [flags] [packages]
3.1.3 单元测试-assert
3.1.4 单元测试-覆盖率
- 如何衡量代码是否经过了足够的测试?
- 如何评价项目的测试水平?
- 如何评估项目是否达到了高水准的测试等级?
运行时在命令最后添加--cover可以得到测试的覆盖率。
Tips
- 一般覆盖率:50%~60%,较高覆盖率80%+。
- 测试分支相互独立、全面覆盖。
- 测试单元粒度足够小,函数单一职责。
3.2 单元测试-依赖
外部依赖 稳定&幂等
3.3 单元测试-文件处理
3.4 单元测试-Mock
GitHub - bouk/monkey: Monkey patching in Go 快速Mock函数
- 为一个函数打桩
- 为一个方法打桩
可以实现测试不依赖于本地文件。
3.5 基准测试
- 优化代码,需要对当前代码分析
- 内置的测试框架提供了基准测试的能力
三、实践练习例子
需求
- 实现一个展示话题(标题,文字描述)和回帖列表的后端http接口;
- 本地文件存储数据
要求
- 支持对话题发布回帖。
- 回帖id生成需要保证不重复、唯一性。
- 新加回帖追加到本地文件,同时需要更新索引,注意Map的并发安全问题。
四、课后个人总结
这节课更多的内容是关于编程及项目方面的思想和方法,Go语言不同于其它语言的新知识,以及部分的操作系统相关知识。本篇文章目前主要内容还是随堂记录,后续会添加更多相关学习内容。