这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
一、本节课重点内容
1. 语言进阶
2. 依赖管理
3. 测试
二、详细知识点介绍
1. 语言进阶
并发
go语言可以充分发挥多核优势,高效运行。
- 并发:多线程程序在单核CPU上运行
- 并行:多线程程序在多核CPU上运行
- 进程:资源管理的最小单元,进程虚拟空间地址分为用户和内核空间
- 线程:资源调度的最小单位,内核态,共享进程中的资源
- 协程:用户态,属于轻量级线程,调度和切换都在用户态
goroutine
go语言提供了sync和channel两种方式支持协程的并发。
goroutine使用简单,只需要在函数前加上关键字go。提倡通过通信共享内存而不是通过共享内存而实现通信。
由于goroutine是异步执行,因此需要同步,否则主程序退出而goroutine还没执行完,常用方式有:Sleep、Channel、Sync。如果事前知道每个协程的执行时间,则可以通过Sleep等待协程执行完再退出,但是在实际开发中,很多时候是不能事先知道协程的执行时间的,因此,在实际开发中经常使用Channel和Sync来同步。
Sync
多个并发协程直接不需要通信,可以使用sync.WaitGroup,主程序Add和协程Done增加和减少计数器。
开启协程+1;执行结束-1;主协程阻塞直到计数器为0.
Channel
使用make(chan 元素类型,[缓冲大小])进行创建,分为无缓冲通道和有缓冲通道。无缓冲通道会阻塞直到接收或者发送,使用“<-”发送和接收。
2. 依赖管理
背景
实际开发不可能基于标准库0~1编码搭建,更多关注的是业务逻辑的实现,常常使用被封装好、经过验证的开发工具或者组件提升开发效率,比如框架、日志、dirver以及collection等一系列以来通过sdk的方式引入,因此需要对依赖包进行管理。
Go依赖管理演进
go语言的以来管理主要经历了三个阶段,分别是GOPATH、Go Vendor、Go Module。最初的GOPATH无法实现package的多版本控制,Go Vendor又无法控制依赖的版本,更新项目又可能出现依赖冲突,因此现在常常使用Go Module。
依赖管理三要素
(1)描述依赖的配置文件
依赖版本包括语义化版本和基于commit的伪版本
语义版本中,不同的MAJOR版本表示是不兼容的API,即使是同一个库,MAJOR版本不同也会被认为是不同的模块;MINOR版本通常是新增函数或功能,支持向后兼容;而path版本一般是修复bug。基于commit的伪版本则是commit的时间戳和一个12位的哈希前缀检验码,每次提交commit后就会默认生成一个伪版本号。
indirect后缀表示go.mod对应的当前模块没有直接导入该依赖模块的包,也就是非直接依赖,即间接依赖
go是1.11实验性引入go module,而主版本2+的模块会在模块路径增加/vN后缀,所以很多包在之前就已经打上了更高版本的tag,为了兼容这些包,对于没有go.mod文件并且主版本2+的依赖,会在版本号后加上+incompatible。对于未遵守这一规则的V2+tag版本的依赖包则会打上compatible。
(2)管理依赖库的中心仓库
go proxy解决了无法保证构建稳定性、无法保证依赖可用性、增加第三方平台负载压力的问题,go proxy是一个服务站点,它会缓存源站中的软件内容,缓存的版本不会改变,并且在源站删除之后依然可用,在构建时会直接从go proxy站点拉取依赖。
GOPROXY是一个Go Proxy站点的URL列表,按照顺序进行查找,“direct”表示源站。
(3)本地工具
- go get:获取包
- go mod:初始化(init)、下载(download)、增加依赖同时删除不需要的依赖(tidy)
- 在代码提交之前尽量执行下go tidy,减少构建时无效依赖包的拉取
三、测试
测试关系着系统的质量,质量则决定着线上系统的稳定性。测试是避免事故的最后一道屏障,只要我们做好完备的测试,就能极大避免事故发生。测试从上到下依次是回归测试、集成测试、单元测试,从上到下覆盖率逐层变大,成本逐层降低
单元测试
单元测试主要包括:输入、测试单元、输出以及校对。单元包括接口、函数、模块等;用最后的校对保证代码的功能和我们预期的相符;单元测试一方面可以保证质量,在整体覆盖率足够的情况下,一定程度保证了新功能本身的正确性,又未破坏原有代码的正确性。另一方面也可以提示效率,可以在一个较短周期内定位和修复bug。
单元测试规则:
- 测试文件以_test.go结尾
- 测试函数以Test开头且连接的首字母大写
- 初始化逻辑放在TestMain中
单元覆盖率指代码覆盖率,主要包括分支、方法、类等指标,实际项目一般要求是50%~60%覆盖率,资金型服务需要达到80%。
Mock测试
复杂项目一般会依赖文件、数据库、缓存等,而单元测试需要依赖本地的文件,如果文件被修改或者删除,测试就会失败。为了保证测试case的稳定性,我们需要对读取文件函数进行mock,屏蔽对文件的依赖。mock测试常使用monkey库,运行时通过Go的unsafe包,将内存中函数的地址替换为运行时函数的地址,跳转到待打桩函数或方法,从而摆脱依赖。
基准测试
基准测试是指一段程序的运行性能及耗费CPU的程度。在实际项目中经常会遇到代码性能瓶颈,为了定位问题需要对代码做性能分析,这个时候就需要用到基准测试。
四、课后个人总结
通过这次课程,了解了go语言高并发、高性能的原理,了解了go语言依赖管理的三要素,当然课后还需要不断练习,不断学习去如何高效使用proxy和go.mod,最后就是了解了测试的流程以及测试的重要性,知道如何对自己的代码进行测试,如何提升测试的代码覆盖率,面对不同的情况使用不同的测试方法。