这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
一、并发编程
1. Goroutine
go语言一次可创建上万条协程,所以可实现高并发编程。GoRoutine在相同的地址空间中运行,因此在访问共享的内存时必须进行同步。
sync 包提供了这种能力。
2. CSP
提倡通过通信共享内存而不是通过共享内存通信
3. Channel
make(chan 元素类型,[缓冲大小])
- 带缓冲的Channel ,方便解决生产和消费速度不同的情况 make(chan int, 2)
- 无缓冲通道 make(chan int)
//通过信道操作符<-进行数据操作
ch <- v //将v发送给信道ch
v := <-ch //从信道ch中取数赋值给v
发送者可通过close关闭信道: close(ch) 接受者可通过循环 for v := range ch 来不断接收信道传送的值,直到信道关闭
select 可选择未阻塞的分支进行,为了在尝试发送或者接收时不发生阻塞,可使用 default 分支:
select {
case i := <-c:
// 使用 i
default:
// 从 c 中接收会阻塞时执行
}
4. 并发安全Lock
lock sync.Mutex
lock.Lock()
x += 1
lock.Unllock()
5. WaitGroup
实现协程间的同步,原理是通过计数器,开启协程+1,执行结束-1, 主协程阻塞直到计数器为0
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i <5; i++ {
go func(j int) {
defer wg.Done() //计数器减一
hello(j)
}(i)
}
wg.Wait()
二、依赖管理
学会站在巨人的肩膀上--依靠第三方工具。那么如何管理依赖库?
1. Go 依赖管理演进
GOPATH -> GO Vendor -> Go Module
不同环境(项目)依赖的版本不同,因此需要控制依赖库的版本。 GOPATH 的结构:
GOPATH项目代码直接依赖src下的代码,所以多个项目依赖于同一个库,依赖的是同一份代码,则不同项目不能依赖同一个库的不同版本。为解决这个问题,GoVendor为每一个项目创建了一份依赖副本,但在下面这种情况,依然会出现问题:
因此目前Go Module是最常用的依赖管理工具,来定义版本规则和管理项目依赖关系。
- 通过go.mod 文件管理依赖包版本
- 通过go get/mod 指令管理依赖包
2. 依赖管理三要素
- 配置文件,描述依赖 go.mod
- 中心仓库管理依赖库 Proxy
- 本地工具 go mod/get
go.mod
- indirect表示该go.mod对应模块,没有直接导入对应的依赖包,属于间接依赖。
- incompatible 表示没有go.mod 且主版本在2以上的依赖
依赖分发-Proxy
依赖分发,即从哪里下载依赖,怎么下载依赖。若直接依赖第三方工具如github, 不能保证依赖的更新,且会增加github平台的压力。因此增加了Proxy作为缓存,提供稳定可靠的服务站点。第一篇文章中提到过配置proxy:
配置了 go mod proxy,参考 goproxy.cn/ 里面的描述配置,下载第三方依赖包的速度可以大大加快
go mod
go mod init 初始化,创建go.mod文件 go mod download 下载模块到本地缓存 go mod tidy 增加需要的依赖,删除不需要的依赖。尽量提交前执行go mod tidy ,减少无效依赖包的拉取。
三、测试
分为回归测试(通过终端回归一些主流程场景)、集成测试(对系统功能维度进行测试)、单元测试,覆盖率逐渐变大。
1. 单元测试
开发者对单元模块做功能测试。 规则:
- 所有测试文件以_test.go结尾
- func TestXxxx(t *testing.T)
- 初始化逻辑放在TestMain中
- 运行时是go test ...
//测试前:数据装载、配置初始化等前量工作
code := m.Run()
//测试后:释放资源等首尾工作
os.Exit(code)
如何评估单元测试? 覆盖率(函数代码的执行行数占比), 一般覆盖率50-60%, 较高覆盖率80%+ 测试分支相互独立、全面覆盖 测试单元粒度足够小,函数单一职责
2. Mock测试
简单来说,单测可能会依赖本地文件,当本地文件发生改变,结果也会变化。为了测试case的稳定,我们对读取文件函数进行Mock,屏蔽对于文件的依赖。
使用工具Monkey
3. 基准测试
测试系统性能和耗费CPU程度 优化代码,需要对当前代码分析
func BenchmarkXxxx( *testing.B)
四、项目实例
1.需求
- 实现一个展示话题(标题,文字描述)和回帖列表的后端http接口;
- 本地文件存储数据
分析: 话题和帖子是一对多的关系,共需要三个表:user, topic, postlist
2. 组件及技术点
-
web框架:Gin - github.com/gin-gonic/g…
Gin : go世界最流行的web框架
-
分层结构设计:github.com/bxcodec/go-…
-
文件操作:读文件pkg.go.dev/io
-
数据查询:索引www.baike.com/wikiid/5527…
为提高查询性能,在服务对外暴露之前,利用元数据构建map索引存放在内存中,
3. 运行代码:
git clone 到本地 Moonlight-Zhao/go-project-example (github.com)
go run sever.go
出现错误:
PS D:\MyGo\src\go-project-example> go run sever.go
sever.go:4:2: bou.ke/monkey@v1.0.2: missing go.sum entry; to add it:
go mod download bou.ke/monkey
sever.go:5:2: bou.ke/monkey@v1.0.2: missing go.sum entry; to add it:
go mod download bou.ke/monkey
sever.go:6:2: bou.ke/monkey@v1.0.2: missing go.sum entry; to add it:
go mod download bou.ke/monkey
..\..\pkg\mod\github.com\gin-gonic\gin@v1.3.0\binding\msgpack.go:12:2: bou.ke/monkey@v1.0.2: missing go.sum entry; to add it:
go mod download bou.ke/monkey
根据提示下载缺失的依赖包后再次运行,出现数据库错误,
2023/01/16 16:44:35 D:/MyGo/src/go-project-example/repository/db_init.go:13
[error] failed to initialize database, got error dial tcp 127.0.0.1:3306: connectex: No connection could be made because the target machine actively refused it.
exit status 0xffffffff
突然想到这个电脑还没有MySql, 遂下载安装,参考链接
执行example.sql文件,
当前数据库:
在cmd中执行curl 测试:
改变了curl命令:
查询数据库,是存在数据的,但查询不到。问题暂时还没解决,挖个坑。
总结
结合实际的项目,感觉自己对网络这一块了解还是太少,很多东西知其然不知其所以然,任重而道远啊。
参考链接
Go 语言入门 - 工程实践 .pptx - 飞书云文档 (feishu.cn)