这是我参与「第五届青训营」笔记创作活动的第6天!
1. Go语言进阶(并发编程)
Goroutine
Go为什么快是因为Go可充分发挥多核优势,高效运行
并发:多线程程序在一个核的cpu上运行
并行:多线程程序在多个核的cpu上运行
协程:用户态,轻量级线程,栈MB级别
线程:内核态,线程跑多个协程,栈KB级别
CSP
协程的通信方式:
Channel
make(chan 元素类型,[缓冲大小])
- 无缓冲通道 make(chan int)
- 有缓冲通道 make(chan int,2)
这里以上课的例子说明,src为无缓冲通道,dest为有缓冲通道。 A协程实现了发送数字0~9,B协程实现了遍历src并输出平方到dest。在这一过程中,设dest为有缓冲通道的作用是缓和生产者与消费者之间的速率不匹配的问题,提高整体程序的效率
并发安全 Lock
对两个实验程序分别进行加锁和正常运行,(个人理解这个加锁操作类似于操作系统中的PV操作,对临界区进行保护)结果显示加锁操作的结果是10000次,符合预期,而不加锁的结果则不符合预期(这个结果是不定的),说明对临界区的权限控制(Lock)能有效的提高并发安全性。
WaitGroup
计数器
开启协程+1;执行结束-1;主协程阻塞直到计数器为0
2. 依赖管理
背景
实际的开发非常复杂,工程项目不可能基于标准库0~1编码搭建,应把更多的精力关注在业务逻辑的实现。其他的一些依赖集合都可以通过SDK的方式进行管理,需要系统的管理依赖库
Go 依赖管理演进
Go的依赖管理经历了如下图三个阶段的变化,这是由不同环境依赖版本不同和控制依赖库版本不同决定的
GOPATH
环境变量GOPATH下三个重要的文件
- bin:项目编译的二进制文件
- pkg: 项目编译的中间产物,加速编译
- src:项目源码
GOPATH的特点
1.项目代码直接依赖src下的代码
2.go get下载最新版本的包到src目录下
GOPATH的弊端
当A和B依赖某一package的不同版本时,无法实现package的多版本控制
Go Vendor
Go Vendor弊端
- 无法控制依赖的版本
- 更新项目有可能出现依赖冲突,导致编译出错
Go Module
- 通过go.mod文件管理依赖包版本
- 通过go get/go mod指令工具管理依赖包
终极目标:定义版本规则和管理项目依赖关系
依赖管理三要素
-
配置文件,描述依赖 go.mod
-
中心仓库管理依赖库 Proxy
-
本地工具 go get/mod
依赖配置——go.mod
依赖配置——version
MAJOR:大版本
MINOR:新增函数或者功能
PATCH:代码bug修复
依赖配置——indirect
其实就是一种非直接依赖
依赖配置——incompatible
- 主版本2+模块会在模块路径增加/VN 后缀
- 对于没有 go.mod 文件并且主版本2+的依赖,会+incompatible
依赖图
依赖分发——回源
直接使用版本仓库进行依赖的下载会产生诸多问题:
- 无法保证构建稳定性增加/修改/删除软件版本
- 无法保证依赖可用性删除软件
- 增加第三方压力代码托管平台负载问题
依赖分发——Proxy
go proxy就是解决这些问题的方案,Go Proxy 是一个服务站点,它会缓存站中的软件内容,缓存的软件版本不会改变
使用Go Proxy之后,构建时会直接从 Go Proxy 站点拉取依赖。
依赖分发——变量GOPROXY
Go Modules通过GOPROXY环境变量控制如何使用 Go Proxy。GOPROXY是一个 Go Proxy 站点URL列表,可以使用“direct“表示源站。会优先从proxy1下载依赖,如果proxy1不存在,后去proxy2寻找,如果proxy2中不存在则会回源到源站直接下载依赖,缓存到proxy站点中。
工具——go get
工具——go mod
3. 测试
测试是避免事故的最后一道屏障
测试的类型
- 回归测试:指通过手动的方式对终端进行操作,比如刷抖音看评论区的反馈
- 集成测试:对系统功能的维度进行测试
- 单元测试:开发者对单独的函数功能模块进行测试
单元测试
单元测试主要包括,输入,测试单元,输出,以及校对,单元的概念比较广,包括接口,函数,模块等;用最后的校对来保证代码的功能与我们的预期相符;单一方面可以保证质量在整体覆盖率足够的情况下,一定程度上既保证了新功能本身的正确性,又未破坏原有代码的正确性。另一方面可以提升效率,在代码有bug的情况下,通过编写单测,可以在一个较短周期内定位和修复问题。
单元测试——规则
- 所有测试以_test.go结尾
- func TestXxx(*testing.T)
- 初始化逻辑放到TestMain中
单元测试——例子
返回的是Jerry则说明出错,通过TestHelloTom函数进行测试
单元测试——assert
可用assert包进行验证
单元测试——覆盖率
- 如何衡量代码是否经过了足够的测试?
- 如何评价项目的测试水准?
- 如何评估项目是否达到了高水准测试等级?
通过代码覆盖率去验证
Tips: 一般覆盖率 : 50%~60%,较高覆盖率80%+。 测试分支相互独立、全面覆盖 测试单元粒度足够小,函数单一职责。
单元测试——依赖
单元测试需要保证稳定性和幂等性,稳定是指相互隔离,能在任何时间,任何环境,运行测试。幂等是指每一次测试运行都应该产生与之前一样的结果。而要实现这一目的就要用到mock机制。
Mock测试
monkey : github.com/bouk/monkey
这里我们用了Monkey,monkey是一个开源的mock测试库,可以对method,或者实例的方法进行mock
打桩可以理解为用一个函数A去替换另一个函数B,B是原函数,A则是打桩函数
基准测试
Go 语言还提供了基准测试框架,基准测试是指测试一段程序的运行性能及耗费 CPU 的程度。而我们在实际项目开发中,经常会遇到代码性能瓶颈,为了定位问题经常要对代码做性能分析,这就用到了基准测试,使用方法类似于单元测试
4. 项目实战
需求设计
4.1 需求描述
社区话题页面
- 展示话题 (标题,文字描述) 和回帖列表
- 暂不考虑前端页面实现,仅仅实现一个本地web服务
- 话题和回帖数据用文件存储
4.2 用例分析
根据需求可以提出两个实体——话题和帖子
4.3 ER图
根据以上话题和帖子两个实体设计相应的结构体
一对多的关系
4.4 分层结构
整体分为三层,repository数据层,service逻辑层,controoler视图层
- 数据层关联底层数据模型,也就是这里的model,封装外部数据的增删改查,我们的数据存储在本地文件,通过文件操作拉取话题和帖子数据。数据层面向逻辑层,对service层透明,屏蔽下游数据差异,也就是不管下游是文件,还是数据库,还是微服务等,对service层的接口模型是不变的
- 逻辑层处理核心业务逻辑,计算打包业务实体entity,对应我们的需求,就是话题页面,包括话题和回帖列表,并上送给视图层
- 视图层负责处理和外部的交互逻辑,以view视图的形式返回给客户端
总结
这一节主要学习了Go语言进阶的并发编程、Go的三种依赖演进和三种常用的测试类型,由于内容很多,所以篇幅较长,需要课下再去消化了解。对于本节课程的实践由于时间原因还没有悟透,这里先对知识点进行总结,后续有时间再对项目进行分析。