这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
语言进阶-并发编程
-
并发-并行——go充分利用多核优势(系统对外的特征)
- 并发:多线程程序在一个核cpu运行
- 并行:多线程程序在多个核cpu运行
-
协程 Goroutine
- 协程:用户态,轻量级线程,栈MB级别
- 线程:内核态,线程跑多个协程,栈KB级别
- 只需要在项目里面加上go func(){}(传入参数) 就是一个协程
go func(j int) { defer wg.Done() hello(j) }(i) time.sleep(time.Second) //保存子线程执行完之前主线程不会退出,暴力做法 -
CSP(Communicating Sequential Processes)协程通信
- 通过通信共享内存而不是通过共享内存实现通信(通道)
-
通道 Channel
-
make(chan 元素类型,[缓冲大小])- 无缓冲通道
make(chan int) - 有缓冲通道
make(chan int,2)
- 无缓冲通道
-
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3) //有缓冲通道,消费者消费速度会比打印更复杂,所以可能会慢一些,用带缓冲队列就不会因为消费者速度问题影响生产效率
go func() { //A协程发送0-9数字
defer close(src) //延迟资源关闭
for i := 0; i < 10; i++ {
src <- i
}
}()
go func() { //B协程计算输入数字的平方
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest { //main协程打印输出
//复杂操作
println(i)
}
}
-
锁 Lock pkg.go.dev/sync
lock sync.Mutex额
-
线程同步 WaitGroup pkg.go.dev/sync
- 昨天的作业有
依赖管理
管理依赖库
-
Gopath
-
go语言配置的环境变量
- bin二进制文件、pkg中间产物、src源码
-
无法实现package的多版本控制(不同的项目调用不同版本的包冲突)
-
-
Go Vendor
- vendor 解释了详细的依赖包,每个项目分开在vender目录下寻找依赖(解决版本冲突)
- 无法控制依赖版本
- 更新项目可能出现依赖冲突,导致编译出错
-
Go Module : go.dev/blog/using-…
- go.mod文件管理依赖包版本
- go get/go mod 指令工具管理依赖包
- 定义版本规则和管理项目依赖关系
-
依赖管理三要素
- 配置文件,描述依赖 go. mod
- 中心仓库管理依赖库 Proxy
- 本地工具 go get/mod
-
go会选择最低的兼容版本(所有依赖中的最新版本、向下兼容)
-
依赖分发
GOPROCY="<https://proxy1.cn>,<https://proxy2.cn>,direct"- 服务站点url列表,“direct代表源站”
-
工具——go get example.org/pkg
- @update 默认
- @none 删除依赖
- @v1.1.2 tag版本、语义版本
- @23dfdd5 特定的commit
- @master 分支最新commit
-
工具——go mod
- init 初始化,创建go.mod文件
- download 下载模块到本地缓存
- tidy 增加需要的依赖删除不需要的依赖
了解 Go 依赖管理演进的历程,通过课程学习以及课后实践能能够熟练使用 go module 管理依赖。
单元测试
-
单元测试
-
单元测试-集成测试-回归测试(从上到下覆盖率逐层变大,成本逐层降低)
-
单元测试组成
- 输入
- 测试单元(函数、模块)
- 输出
-
优点
- 保证质量
- 提升效率(定位问题、防止损失扩大化)
-
规则
- _test.go结尾
func TestXxx(*testing.T)- 初始化逻辑在TestMain中
-
覆盖率:判断单元测试有效性
func TestManyGo(t *testing.T) {
//测试前:数据装载、配置初始化
ManyGo()
//测试后、释放资源等收尾工作
}
-
Mock 测试
-
快速mock函数
- 为一个函数打桩
- 为一个方法打桩
- 不依赖本地文件
-
基准测试
-
随机对函数执行基准测试
-
优点
- 优化代码,需要对当前代码分析
- 内置的测试框架都提供了基准测试的能力
项目实战
需求模型来源
青训营话题页forum.juejin.cn/youthcamp/p…
需求
- 实现一个展示话题(标题,文字描述)和回帖列表的后端 http 接口;
- 本地文件存储数据
组件及技术点
-
web 框架:Gin - github.com/gin-gonic/g…
- 了解go web框架的简单使用
-
分层结构设计:github.com/bxcodec/go-…
- 了解分层设计的概念
- repository 数据层 model 外部数据的增删改查
- service 逻辑层 entity 处理核心业务逻辑输出
- controller 视图层 view 处理和外部交互逻辑
-
文件操作:读文件pkg.go.dev/io
-
数据查询:索引www.baike.com/wikiid/5527…
代码:repository-index
初始化话题数据索引
package repository
import (
"bufio"
"encoding/json"
"os"
)
var (
topicIndexMap map[int64]*Topic
postIndexMap map[int64][]*Post
)
func initTopicIndexMap(filePath string) error {
open, err := os.Open(filePath + "topic") //打开文件
if err != nil {
return err
}
scanner := bufio.NewScanner(open)
topicTmpMap := make(map[int64]*Topic) //迭代器对数据遍历刻入结构体产生索引
for scanner.Scan() {
text := scanner.Text()
var topic Topic
if err := json.Unmarshal([]byte(text), &topic); err != nil {
return err
}
topicTmpMap[topic.Id] = &topic
}
topicIndexMap = topicTmpMap
return nil
}