持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
并发编程
1.1 Go Routine
并发&并行 并发主要是在一个CPU核各线程的来回切换 并行主要是多核并行 协程&线程 协程:用户态,轻量级线程,栈为MB级别 线程:用户态,一个线程可以跑多个协程,栈为KB级别
func closure1() {
for i := 0; i < 3; i++ {
go func(j int) {//加上go,就能为函数创建了一个协程
println(j)//为的是快速打印
}(i)
}
time.Sleep(3 * time.Second)//子协程执行完之前,父协程不退出
}
1.2 CSP(Communicating Sequential Processes)
提倡实现通信->共享内存
Goroutine是一个Go实现并发的执行体,通道进行链接,遵循FCFS
但是仍保留旧方式,通过互斥量给进程加锁获取临界区权限,参考操作系统进程线程相关内容
1.3 Channel
这是一种引用类型,用make关键字创建 make(chan元素类型,缓冲大小) ·无缓冲通道 make(chan int) ·有缓冲通道 make(chan int,2)
无缓冲通道会导致同步化,有缓冲通道会发生阻塞(生产者消费者模型)
1.4 并发安全Lock
避免对共享内存做非并发安全读写操作
1.5 WaitGroup
1.6 总结
3个方面,一个是协程,通过高效调度模型实现高并发排作,一个是通道channel,通过通信实现共享内存,最后sync相关关键字,实现并发安全操作和协程间的同步。
2 依赖管理
依赖演进: GOPATH -> GO Vendor -> Go Module 目的是为了定义版本规则和管理项目依赖关系
2.1 GOPATH
场景:方法A和方法B依赖于某一package的不同版本。
问题:无法实现package的多版本控制
2.2 Go Vender
弊端:
问题:
- 无法控制依赖的版本。
- 更新项目又可能出现依赖冲突,导致编译出错。
2.3 Go Module 管理方案(现行)
- 通过go.mod 文件管理依赖包版本
- 通过go get/go mod 指令工具管理依赖包
2.4 依赖管理三要素
1.配置文件,描述依赖 go.mod
2.中心仓库管理依赖库 Proxy
3.本地工具 go get/mod
2.5 依赖配置 go.mod
后面路径+版本号 //indirect 表示非直接依赖
前面v1.0.0是语义化版本,后面是基于commit的伪版本
${MAJOR}.${MINOR}.${PATCH}
v×.0.0-yyyymmddhhmmss-abcdefgh1234 12位的哈希校验码前缀
- 主版本2+模块会在.模块路径增加/vN后缀。
- 对于没有go.mod文件并且主版本2+的依赖,会+incompatible,用来兼容在加/vN之前已经打上2以上tag的依赖
2.6 依赖分发-变量 GO PROXY
go proxy是一个服务站点,它会缓存源站中的软件内容,缓存的软件版本不会改变,并且在源站删除之后依然可用,从而实现了供immutability和available的依赖分发,使用Go Proxy之后,构建时会直接从Go Proxy站点拉取依赖,而不用从代码托管平台拉取。
按Proxy1->Proxy2->Direct 查找依赖,两个Proxy都没有回源到第三方托管平台
2.7 go get
2.8 go mod(重点)
- init 初始化,创建go.mod文件
- download 下载模块到本地缓存
- tidy 增加需要的依赖,删除不需要的依赖(每次提交代码之前都可以执行一下)
go mod init "项目名称" // 初始化modules
go mod tidy //检测依赖
go mod download //下载依赖
3.测试
测试一般分为,回归测试一般是叫同学手动通过终端回归一些固定的主流程场景
集成测试是对系统功能维度做测试验证
单元测试测试开发阶段,开发者对单独的函数、模块做功能验证,层级从上至下
成本逐渐减低,而测试覆盖率确逐步上升,所以单元测试的覆盖率一定程度上决定这代码的质量。
3.1 单元测试
如何衡量代码是否经过了足够的测试?
如何评价项目的测试水准?
如何评估项目是否达到了高水准测试等级?
覆盖率
- 一般覆盖率:50%~60%,较高覆盖率80%+
- 测试分支相互独立、全面覆盖
- 测试单元粒度足够小,函数单一职责
3.2 单元测试-依赖
3.3 mock
如果我们的单测需要依赖本地的文件,如果文件被修改或者删除测试就会fail。为了保证测试case的稳定性,我们对读取文件函数进行mock,并屏蔽对于文件的依赖(在任何时间任何地点执行)。
函数原型
这里我们用了Monkey,monkey是一个开源的mock测试库,可以对method,或者实例的方法进行mock,反射和指针赋值
Mockey Patch的作用域在Runtime,在运行时通过通过Go的unsafe包,能够将内存中函数的地址替换为运行时函数的地址。将待打桩函数或方法的实现跳转到。
基准测试
基准测试是指测试一段程序的运行性能及耗费CPU的程度。而我们在实际项目开发中,经常会遇到代码性能瓶颈,为了定位问题经常要对代码做性能分析,这就用到了基准测试。
例子:
基准测试以Benchmark开头入参是testing.B,用b中的N值反复递增循环测试
(对一个测试用例的默认测试时间是1秒,当测试用例函数返回时还不到1秒,那么testingB中的N值将按1、2、5、10、20、50…递增,并以递增后的值重新进行用例函数测试。)
ResetTimer重置计时器,我们在reset之前做了其他的准备操作,这些操作不位该作为基准测试的范围
runparallel是多协程并发测试,执行2个基街财试,发现代码在并发情况下存在劣化、主要原因是rand为了保证全局的随机性和并发安全,持有了1把全局锁。
高性能随机数方法fastrand,牺牲了数列一致性github.com/bytedance/g…
func FastSelect() int {
return ServerIndex[fastrand.Intn( n: 10)]
}
4.项目实战
需求:社区话题页面 √展示话题(标题,文字描述)和回帖列表 √暂不考虑前端页面实现,仅仅实现一个本地web服务 √话题和回帖数据用文件存储
用例图
ER图
分层结构
整体分为三层,repository数据层,service逻辑层,controler视图层
repository数据层关联底层数据模型,封装外部数据CRUD,我门的数据存储在本地文件,通过文件操作拉取话题,帖子数据,数据是面向逻辑层,对service层透明,屏蔽下游数据差异,不论下游是文件,还是数据库,还是微服务等,对service层的接口模型是不变的。
Servcie逻辑层处理核心业务逻辑,计算打包业务实体entiy,对应我们的需求,就是话题页面,包括话题和回例表,并上送给视图层;
Controller视图层负责处理和外部的交互逻辑,以view视图的形式返回给客户端,对于我们需求,我们封装json格式化的请求结果,api形式访问就好。
用到的工具
Gin高性能go web框架 github.com/gin-goniclg…
Go Mod go mod init go get gopkg.in/g in-goniclgin. v l @ v l.3.0
基于gin搭建web服务器,这里我们主要涉及路由分发,不会涉及其他复杂的概念。
因为我们引入了web框架,所以就涉及go module管理,我们首先通过go mod是初始go mod管理配置文件,然后go get下载gin依赖
QA
协程可以理解为用户线程,为了和内核态线程区分,go在用户态实现了 token鉴权直接服务端,cookie和session要后台