Go 语言进阶
这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天
1 语言进阶
Go 可以充分发挥多核优势,高效运行
1.1 Goroutine
线程:用户态,轻量级线程,栈 MB 级别。 协程:内核态,线程跑多个协程,栈 KB 级别。
1.2 CSP (Communicating Sequential Processes)
提倡通过通信共享内存而不是通过共享内存而实现通信
1.3 Channel
make(chan元素类型,[缓冲大小])
- 无缓冲通道 make(chan int)
- 有缓冲通道 make(chan int,2)
1.4 并发安全
1.5 WaitGroup
计数器 开启协程 +1;执行结束 -1;主协程阻塞直到计数器为 0。
让我们回到最初多个协程带打印 hello gorouting 的例子,现在我们用 watingroup 实现协程的同步阻塞。如刚才所说,首先通过 add 方法,对计数器 +5,然后开启协程,每个协程执行完后,通过 done 对计数器减少 1, 最后 wait 主协程阻塞,计数器为 0 退出主协程。右边是最终的输出结果。
2 依赖管理
依赖指各种开发包,我们在开发项目中,需要学会站在巨人的肩膀上,也就是利用已经封装好的、经过验证的开发组件或工具来提升自己的研发效率。
对于 hello world 以及类似的单体函数只需要依赖原生 SDK,而实际工程会相对复杂,我们不可能基于标准库 0~1 编码搭建,而更多的关注业务逻辑的实现,而其他的涉及框架、日志、driver、 以及 collection 等一系列依赖都会通过 sdk 的方式引入,这样对依赖包的管理就显得尤为重要。
2.1 Go依赖管理演进
- 不同环境(项目)依赖的版本不同
- 控制依赖库的版本
2.1.1 GOPATH
首先是 GOPATH,GOPATH 是 Go 语言支持的一个环境变量,value 是 Go 项目的工作区。目录有以下结构:
- src:存放Go项目的源码
- pkg:存放编译的中间产物,加快编译速度
- bin:存放Go项目编译生成的二进制文件
2.1.2 Go Vender
- 无法控制依赖的版本。
- 更新项目又可能出现依赖冲突,导致编译出错。
2.1.3 Go Module
Go Modules 是 Go 语言官方推出的依赖管理系统,解决了之前依赖管理系统存在的诸如无法依赖同一个库的多个版本等问题,go module 从 Go 1.11 开始实验性引入,Go 1.16 默认开启,一般都读为 go mod。
2.2 依赖管理三要素
2.2.1 依赖配置
首先模块路径用来标识一个模块,从模块路径可以看出从哪里找到该模块,如果是 github 前缀则表示可以从 Github 仓库找到该模块,依赖包的源代码由 github 托管,如果项目的子包想被单独引用,则需要通过单独的 init go。mod 文件进行管理。下面是依赖的原生 sdk 版本最下面是单元依赖,每个依赖单元用模块路径+版本来唯一标示。
2.2.2 依赖配置 - version
gopath 和 govendor 都是源码副本方式依赖,没有版本规则概念,而 gomod 为了放方便管理则定义了版本规则,分为语义化版本和基于 commit 的伪版本;其中语义化格式如下,不同的 MAJOR 版本表示是不兼容的 API,所以即使是同一个库,MAJOR 版本不同也会被认为是不同的模块; MINOR 版本通常是新增函数或功能,向后兼容;而 patch 版本一般是修复 bug ;而基于 commit 的为版本格式如下,基础版本前缀是和语义化版本-样的;时间戳(yyyymmddhhmmss),也就是提交Commit的时间,最后是校验码(abcdefabcdef),包含12位的哈希前缀;每 次提交commit后Go都会默认生成-个伪版本号。
2.2.3 依赖分发
下面讲一下 go module 的依赖分发。也就是从哪里下载,如何下载的问题。 github 是比较常见给的代码托管系统平台, 而Go Modules 系统中定义的依赖,最终可以对应到多版本代码管理系统中某一项目的特定提交或版本,这样的话,对于 go.mod 中定 义的依赖,则直接可以从对应仓库中下载指定软件依赖,从而完成依赖分发。
但直接使用版本管理仓库下载依赖,存在多个问题,首先无法保证构建确定性:软件作者可以直接代码平台增加/修改/删除软件版本,导致下次构建使用另外版本的依赖,或者找不到依赖版本。无法保证依赖可用性:依赖软件作者可以直接代码平台删除软件,导致依赖不可用;大幅增加第三方代码托管平台压力。
而 go proxy 就是解决这些问题的方案,Go Proxy 是一个服务站点,它会缓源站中的软件内容,缓存的软件版本不会改变,并且在源站软件删除之后依然可用,从而实现了供 "immutability" 和 "available" 的依赖分发;使用 Go Proxy 之后,构建时会直接从 Go Proxy 站点拉取依赖。
下面讲一下 go proxy 的使用,Go Modules 通过 GOPROXY 环境变量控制如何使用 GoProxy; GOPROXY 是一个 Go Proxy 站点 URL 列表,可以使用 "direct" 表示源站。对于示例配置,整体的依赖寻址路径,会优先从proxy1 下载依赖,如果 proxy1 不存在,后下去 proxy2寻找,如果 proxy2 中不存在则会回源到源站直接下载依赖,缓存到 proxy 站点中。