这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
本堂课重点内容
- 语言进阶:了解Go高性能本质
- 依赖管理:了解Go语言依赖管理的演进路线
1.语言进阶
1.1 Goroutine
协程与线程
协程:
- 用户态
- 一个线程跑多个协程
- 栈KB级别
线程:
- 内核态
- 栈MB级别
语法
go 函数调用即可开启一个goroutine
1.2 CSP(Communicating Sequential Process)
两种通信方式
- 通过通道通信共享内存
- 通过共享内存实现通信
提倡通过通道通信共享内存而不是后者
原因如下:
- 共享内存需要不同goroutine访问同一个临界区,访问临界区需要加锁,降低并行的效率
1.3 Channel
Go中的通道,用于goroutine间的通信
分为有缓冲区和无缓两种
创建
make(chan 元素类型, [缓冲大小])
// 有缓
make(chan int)
// 无缓
make(chan int, 2)
区别
- 无缓冲通道会使得goroutine双方同步化,故称为同步通道
- 有缓冲区的通道是一个典型的生产者-消费者模型
- 当缓冲区满了以后,直到有goroutine从中取走数据,发送方都无法继续发送从而阻塞
- 同理,当缓冲区空的时候,直到有goroutine向通道发送数据,接收方都无法继续接收从而阻塞
使用
src := make(chan int)
go func(data []int){
defer close(src)
for _, item := range data{
src ← item
}
}()
go func(){
for item := range src{
// 使用数据
}
}
1.4 并发安全Lock
对临界区的操作必须要加锁,否则结果无法预计
WaitGroup
作用:阻塞直到所有调用它的goroutine完成后,才继续执行后面的程序
涉及标准库:sync
三个公开的方法:
- Add(delta int) // wg计数器+delta
- Done() // 计数器-1
- Wait() // 阻塞直到计数器为0
使用
func ManyGoWait(){
var wg sync.WaitGroup
wg.Add(5)
for i: = 0; i < 5; i++ {
go func(j int){
defer wg.Done() // goroutine结束后,调用使得计数器-1
Println(j)
}(i)
}
wg.Wait() // 阻塞 直到所有goroutine结束
}
2.依赖管理
背景动机:
- 工程项目需要借助外部库搭建
- 管理所依赖外部库的版本
2.1 依赖管理演进
2.1.1 GOPATH
原理
- GOPATH是Go语言支持的一个环境变量,值是项目工作区
文件结构
$GOPATH
- bin // 可执行文件
- pkg // 项目编译中间产科,加速编译
- src // 项目源码
结果
- 项目代码直接依赖src下的代码
- go get下载最新版本的包到src目录
弊端
- 无法实现package的多版本控制
2.1.2 Go Vendor
原理
- 项目目录下增加vendor文件,所有依赖包以副本形式放在$ProjectRoot/vendor下
- 依赖寻址方式:优先vendor文件,再GOPATH
文件结构
$ProjectRoot
- vendor
- READEME.md
- main.go
- service
结果
- 通过每个项目引入一份依赖的副本,解决了多个项目需要依赖同一个package的版本冲突问题
弊端
- 无法控制依赖的版本
- 更新项目又可能出现依赖冲突,导致编译出错
- 情景:项目内多个包依赖同一个包的不同版本
2.1.3 Go Module
原理
- 通过go.mod文件管理依赖包版本
- 通过go get/go mod指令工具管理依赖包
2.2 依赖管理三要素
依赖管理三要素
- 配置文件,描述依赖→go.mod
- 中心仓库管理依赖库→Proxy
- 本地工具→go get/mod
2.3 Go Module介绍
2.3.1 依赖配置go.mod
配置
- 依赖管理基本单元(可以视为项目根目录)
- 原生库:指定Go版本
- 单元依赖:每个依赖单元用模块路径+版本号唯一标识
- indirect表示非直接引用:A→B→C(A间接引用了C)
- +incompatible
- 主版本2+模块会在模块路径增加/vN 后缀
- 对于没有 go.mod 文件并且主版本2+的依赖,会+incompatible
2.3.2 依赖配置-version
语义化版本:${MAJOR}.${MINOR}.${PATCH}
- V1.3.0
- V2.4.0
基于commit的伪版本:vX.0.0-yyyymmddhhmmss-abcdefgh1234
- v0.0.0-20230117100156-dasdasda2313
- v1.0.0-20240618125645-cxzbnmbm4547
2.3.3 依赖分发-回源
直接使用版本仓库的下载管理存在的问题
- 无法保证构建稳定性:增加/修改/删除软件版本
- 无法保证依赖可用性:删除软件
- 增加第三方压力:代码托管平台负载问题
依赖分发-Proxy
2.3.6 依赖分发-变量 GOPROXY
优先级递减:Proxy 1→Proxy 2→Direct(源站) Proxy 1/2指服务站点URL列表 从源站下载的依赖会缓存到proxy站点中
2.3.7 工具-go get
go get example.org/pkg@
- update 默认
- none 删除依赖
- v1.1.2 tag版本,语义版本
- 23dfdd5 特定的commit
- master 分支最新的commit
go mod
- init 初始化 建立go.mod文件
- download 下载模块到本地缓存
- tidy 增加需要的依赖,删除不需要的依赖