这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
一、Go语言高效原因解密
并发与并行 并发:多线程程序在单核CPU上切换运行 并行:多线程程序在多核CPU上同时运行 并行是实现并发的一种手段
1.1Goroutine
协程:用户态,轻量级线程,栈KB级别 线程:内核态,一个线程能跑多个协程,栈MB级别 线程是系统中比较昂贵的一种系统资源,创建、切换、停止均是比较重的系统操作 协程的创建和调度由go语言本身来完成 因此go语言可以轻松处理上万的并发量
调用函数时在函数前加go关键字,可以为函数创建一个协程来运行
1.2CPS(Communicating Sequential Processes)
协程之间的通信提倡通过通信共享内存而不是通过共享内存而实现通信
1.3Channel
Channel是一种引用类型,需要通过make关键字创建 创建语法make(chan 元素类型, [缓冲大小]) 若无第二个参数,则为无缓冲通道 添加元素语法: src <- i 若消费端速度慢于生产端,则使用有缓冲通道 若消费端速度快于生产端,则使用无缓冲通道
无缓冲通道会导致发送方Goroutine和接收方Goroutine同步化,因此也被称为同步通道 使用带有缓冲区的有缓冲通道即可解决同步问题
1.4并发安全Lock
可以使用sync包下的Mutex来保证协程安全
1.5WaitGroup
go语言中可以通过WaitGroup实现协程同步 其内部维护了一个计数器 开启协程计数器+1;执行结束计数器-1;阻塞直到计数器为0
var wg = sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++{
go fun(j int){
defer wg.Done()
hello(j)
}(i)
wg.Wait()
}
复制代码
二、Go依赖管理
2.1Go依赖管理演进
GOPATH -> Go Vendor -> Go Module 不同环境(项目)依赖的版本不同 控制依赖库的版本
2.1.1GOPATH
bin放项目编译的二进制文件 pkg放项目编译的中间产物,加速编译 src放项目源码 项目直接依赖src下的代码,无法实现package的多版本控制
2.1.2Go Vendor
在项目目录下增加Vendor文件 依赖寻址方式:Vendor => GOPATH 通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题 弊端: 无法控制依赖的版本 更新项目又可能出现依赖冲突,导致编译错误
2.1.3Go Module
通过go.mod文件管理依赖包版本 通过go get/go mod指令工具管理依赖包 终极目标:定义版本规则和管理项目依赖关系
2.2依赖管理三要素
1.配置文件,描述依赖 go.mod 2.中心仓库管理依赖库 Proxy 3.本地工具 go get/mod
2.2.1依赖配置-go.mod
依赖管理基本单元 原生库 单元依赖
2.2.2依赖配置-version
有两种形式 1.语义化版本 MAJOR :不同MAJOR之间不兼容,即代码隔离 MINOR :做一些新增函数和功能,可以做到前后兼容 PATCH :做一些代码bug的修复 2.基于commit的伪版本 版本前缀 :和语义化版本一样 时间戳 :提交某次commit的一个时间戳 十二位hash码前缀
2.2.3依赖配置-indirect
indirect表示间接依赖
2.2.4依赖配置-incompatible
主版本2+模块会在模块路径后面增加/vN后缀 对于没有go.mod文件并且主版本2+的依赖,会+incompatible
2.2.5依赖分发-回源
无法保证构建稳定性 无法保证依赖可用性 增加第三方压力
2.2.6依赖分发-Proxy
Proxy是一个服务站点,会缓存源站中的软件内容 稳定、可靠
2.2.7依赖分发-变量GOPROXY
go.mod通过GOPROXY环境变量来控制Proxy配置 GOPROXY是一个服务站点url列表,若都没有找到则回源到源站
Example: GOPROXY = "proxy.cn,https://proxy2.cn,…" 查找依赖路径: Proxy1 => Proxy2 => direct(第三方源站)
2.2.8工具-go get
直接go get会默认拉取MAJOR版本的最新的一个提交 @none 表示删除依赖 @v1.1.2 tag版本,会拉取特定语义版本 @23dfdd5 特定的commit @master 拉取分支的最新commit
2.2.9工具-go mod
init 初始化,创建go.mod文件 download 下载模块到本地缓存 tidy 增加需要的依赖,删除不需要的依赖 --- 比较常用
三、测试
单元测试能够保证代码质量和提升效率 所有的测试文件都以_test.go结尾 func TestXxx(t *testing.T) 初始化逻辑放到TestMain()中
在TestMain()中进行数据装装载、配置初始化等前置工作,释放资源等收尾工作
单元测试Tips: 一般覆盖率:50%-60%,较高覆盖率80%+ 测试分支相符独立,全面覆盖 测试单元粒度足够小,函数单一职责
******* 国内go get指令容易超时,可通过添加GoProxy代理解决 添加代理指令: go env -w GOPROXY=goproxy.cn
单元测试-依赖
单元测试目标 幂等:重复运行一个case时,结果唯一 稳定:单元测试相互隔离
单元测试-Mock
为减轻对测试文件的强依赖,使用一个打桩函数来替代某个被测试函数