Go 语言上手 - 工程实践、
这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。
语言进阶
并发VS并行
并发:主要是多线程程序在单核CPU上通过时间片切换达到同时运行的状态。
并行:主要是使用多核CPU实现多线程同时运行的状态。
Goroutine
协程:用户态,轻量级线程,栈KB级别。
线程:内核态,线程跑多个协程,栈MB级别。
go开启协程运行函数。
CSP(Communicating Sequential Processes)
Go提倡通过通信来共享内存。
Channel
make创建channel,可以有缓冲。
无缓冲通道也就是同步通道。
缓冲满了会阻塞发送。
带缓冲的channel可以解决生产消费效率不同的问题。
并发安全Lock
sync.Mutex锁。
Lock()加锁。
Unlock()解锁。
WaitGroup
sync.WaitGroup通过计数器等待。
Add()计数器加多少。
Done()计数器减1。
Wait()等待到计数器为0。
依赖管理
目标
- 实现不同环境(项目)依赖的版本不同。
- 控制依赖库的版本。
Go Module
通过go.mod文件管理依赖包版本。
通过go get/mod指令工具管理依赖包。
依赖管理三要素
- 配置文件,描述依赖
go.mod - 中心仓库管理依赖库
Proxy - 本地工具
go get/mod
go.mod
module标识模块。
go原生库
require单元依赖。
依赖标识:[Module Path][Version/Pseudo-version]
version
语义化版本V[Major].[Minor].[Patch]
基于commit伪版本[语义化版本]-yyyymmddhhmmss-abcdefgh1234
indirect
表明不是直接导入的依赖,是间接依赖。
incompatible
主版本2+模块会在模块路径增加/vN后缀。
没有go.mod文件且主版本2+的依赖,会+incompatible,表示可能不兼容。
依赖图
Minor版本一般都是向下兼容的。
Go会选择最低的兼容版本。
依赖分发
Proxy站点缓存依赖代码,直接从Proxy拉取依赖。
通过GOPROXY变量配置服务站点URL列表。
按配置顺序查找。
go get
go get example.org/pkg后面加
- @update 默认
- @none 删除依赖
- @
[version]tag版本,语义版本 - @
[commit提交]特定的commit - @
[分支]分支的最新commit
go mod
go mod后面加
- init 初始化,创建go.mod文件
- download 下载模块到本地缓存
- tidy 增加需要的依赖,删除不需要的依赖
测试
回归测试
正常使用场景测试。
集成测试
对系统功能自动化测试。
单元测试
开发者对函数模块做测试验证。
通过输入输出与期望输出的校对来测试代码的正确性。
测试单元粒度足够小,函数单一职责。
单元测试-规则
- 所有测试文件以_test.go结尾
- func TestXxx(*testing.T)
- 初始化逻辑放到
TestMain中
assert
通过断言校对。
代码覆盖率
go test [测试文件] [代码文件] --cover输出覆盖率。
Mock
- 幂等 多次运行单元测试结果是一样的。
- 稳定 单元测试是相互隔离的,任何时间任何地点都能独立运行。
Patch为函数打桩,在测试时调用打桩函数。
Unpatch卸载。
基准测试
基准测试以Benchmark开头。
BenchmarkXxx(*testing.B)
InitServerIndex()开始。
ResetTimer()重置计时。
RunParallel()并行执行。
fastrand牺牲一些随机数列一致性,提升高并发速度。
项目实践
分层结构
- 数据层Repository:外部数据的增删改查。
- 逻辑层Service:处理核心业务逻辑输出。
- 视图层Controller:处理和外部的交互逻辑。
组件工具
- Gin go web框架
索引
数据行 -> 内存Map
通过将数据存到内存map内实现索引。
查询
sync.Once高并发场景中只执行一次,类似单例模式。
Router
- 初始化数据索引
- 初始化引擎配置
- 构建路由
- 启动服务