Go语言进阶 | 青训营笔记

62 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

一、并发编程

1. Goroutine

image.png 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 的结构:

image.png GOPATH项目代码直接依赖src下的代码,所以多个项目依赖于同一个库,依赖的是同一份代码,则不同项目不能依赖同一个库的不同版本。为解决这个问题,GoVendor为每一个项目创建了一份依赖副本,但在下面这种情况,依然会出现问题:

image.png

因此目前Go Module是最常用的依赖管理工具,来定义版本规则和管理项目依赖关系。

  • 通过go.mod 文件管理依赖包版本
  • 通过go get/mod 指令管理依赖包

2. 依赖管理三要素

  • 配置文件,描述依赖 go.mod
  • 中心仓库管理依赖库 Proxy
  • 本地工具 go mod/get

go.mod

image.png

  • 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. 单元测试

开发者对单元模块做功能测试。 规则:

  1. 所有测试文件以_test.go结尾
  2. func TestXxxx(t *testing.T)
  3. 初始化逻辑放在TestMain中
  4. 运行时是go test ...
//测试前:数据装载、配置初始化等前量工作
code := m.Run()
//测试后:释放资源等首尾工作

os.Exit(code)

如何评估单元测试? 覆盖率(函数代码的执行行数占比), 一般覆盖率50-60%, 较高覆盖率80%+ 测试分支相互独立、全面覆盖 测试单元粒度足够小,函数单一职责

2. Mock测试

简单来说,单测可能会依赖本地文件,当本地文件发生改变,结果也会变化。为了测试case的稳定,我们对读取文件函数进行Mock,屏蔽对于文件的依赖。

使用工具Monkey

3. 基准测试

测试系统性能和耗费CPU程度 优化代码,需要对当前代码分析

func BenchmarkXxxx( *testing.B)

四、项目实例

1.需求

  1. 实现一个展示话题(标题,文字描述)和回帖列表的后端http接口;
  2. 本地文件存储数据

分析: 话题和帖子是一对多的关系,共需要三个表:user, topic, postlist

2. 组件及技术点

image.png

为提高查询性能,在服务对外暴露之前,利用元数据构建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文件,

image.png 当前数据库:

image.png

在cmd中执行curl 测试:

image.png 改变了curl命令:

image.png 查询数据库,是存在数据的,但查询不到。问题暂时还没解决,挖个坑。

image.png

总结

结合实际的项目,感觉自己对网络这一块了解还是太少,很多东西知其然不知其所以然,任重而道远啊。

参考链接

juejin.cn/post/718822…

‍​⁤⁤⁣​​⁡⁢​⁡‍⁢‍⁡⁣​⁢‍‍⁡⁤⁡​‍‬​‌‌⁣‌‌‌⁡⁡‬‍⁢‬⁡⁣‬‬‬‬⁣Go 语言入门 - 工程实践 .pptx - 飞书云文档 (feishu.cn)