[Go语言进阶-工程进阶 | 青训营笔记]
这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
一、本课重点内容
- 语言进阶
- Goroutine
- CSP
- Channel
- Lock
- WaitGroup
- 依赖管理
- 背景
- Go 依赖管理演进
- Go Moudule 实践
- 测试
- 单元测试
- Mock 测试
- 基准测试
- 项目实战
- 需求设计
- 代码开发
- 测试运行
二、详细知识点介绍
2.1 并发 VS 并行
- 并发:多线程程序在单核 CPU 上运行
- 并行:多线程程序在多核 CPU 上运行
Go 可以充分发挥多核优势,高效运行。
2.1.1 Goroutine
- 协程:用户态,轻量级线程,栈 MB 级别
- 线程:内核态,线程跑多个协程,栈 KB 级别
函数前面加上 go 关键字,创建一个协程来执行。
2.1.2 CSP (Communicating Sequential Processes)
提倡使用通信共享内存而不是使用共享内存通信。
2.1.3 Channel
make (chan ElemType, [size])
- 无缓冲通道 如:
make (chan int) - 有缓冲通道 如:
make (chan int, 2)
2.1.4 并发安全 Lock
生产者消费者等
2.1.5 WaitGroup
实现并发线程的同步
Add(delta int) 计数器加 delta
Done() 计数器减1
Wait() 阻塞直到计数器为0
计数器:开启协程+1,执行结束-1,主协程阻塞直到计数器为0。
2.2 依赖管理
2.2.1 背景
- 工程项目后不可能基于标准库0~1编码实现
- 管理依赖库
Go 依赖管理演进:GOPATH Go Vender Go Moudule
GOPATH
- 环境变量
$GOPATH - 项目代码直接依赖
src下的代码 go get下载最新版本的包到src目录下- 弊端:无法实现 package 的多版本控制
Go Vendor
- 依赖包副本形式放在
ProjectRoot/vendor - 依赖寻址方式:
vendorGOPATH - 通过每个项目引入一份依赖的副本,解决了多个项目需要同一个 package 依赖的冲突问题
- 弊端:无法控制依赖的版本,更新项目又可能出现依赖冲突,导致编译出错
Go Moudule
- 通过
go.mod文件管理依赖包版本 - 通过
go get/go mod指令工具管理依赖包
2.2.2 依赖管理三要素
- 配置文件,描述依赖:
go.mod - 中心仓库管理依赖库:
Proxy - 本地工具:
go get/mod
2.3 Go Moudule 实践
2.3.1 依赖配置 - go.mod
module example/project/app
go 1.16
require(
example/lib1 v1.0.2
example/lib2 v1.0.0
example/lib3 v0.1.0-20190725025543-5a5fe074e612
example/lib4 v3.2.0+incompatible
)
2.3.2 依赖配置 - version
语义化版本 ${MAJOR}.${MINOR}.${PATCH}
基于 commit 伪版本 vX.0.0-yyyymmddhhmmss-abcdefgh1234
2.3.3 依赖配置 - indirect
A B C:A B 是直接依赖,A C 是间接依赖。
2.3.4 依赖配置 - incompatible
主版本2+模块会在模块路径增加 /vN 后缀;对于没有 go.mod 文件并且主版本2+的依赖,会+incompatible。
2.3.5 依赖分发 - 回源
- 无法保证构建稳定性(增加/删除/修改软件版本)
- 无法保证依赖可用性(删除软件)
- 增加第三方压力(代码托管平台负载问题)
2.3.6 依赖分发 - Proxy
GOPROXY="https://proxy1.cn, https://proxy2.cn, direct"
2.3.7 工具 - go get
go get example.org/pkg
@update默认@none删除依赖@v1.1.2tag版本,语义版本@23dfdd5特定的 commit@master分支最新的 commit
2.3.8 工具 - go mod
init初始化,创建go.mod文件download下载模块到本地缓存tidy增加需要的依赖,删除不需要的依赖
2.4 测试
2.4.1 单元测试 - 规则
- 所有测试文件以
_test.go结尾 FuncTestXxx(*testing.T)- 初始化逻辑放到
TestMain中
2.4.2 单元测试 - 运行
go test [flags][packages]
2.4.3 单元测试 - 覆盖率
- 一般覆盖率 50%~60%, 较高覆盖率 80%+
- 测试分支相互独立、全面覆盖
- 测试单元粒度足够小,函数单一职责
2.4.4 单元测试 - 依赖
外部依赖 稳定&幂等
2.4.5 单元测试 - Mock
快速 Mock 函数:为一个函数/方法打桩
monkey: github.com/bunk/monkey
三、实践练习例子
3.1 需求描述
- 展示话题和回帖列表
- 暂不考虑前端页面实现,仅仅实现一个本地 web 服务
- 话题和回帖数据用文件存储
3.2 分层结构
- 数据层:数据 Model,外部数据的增删改查
- 逻辑层:业务 Entity,处理核心业务逻辑输出
- 视图层:视图 view,处理和外部的交互逻辑
(源代码见 github.com/Moonlight-Z…
四、课后个人总结
本课程主要学习了 Go 语言的进阶知识,包括并行与并发、协程、依赖管理和项目设计。