这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。
课程内容分为四部分,
- 并发编程
- 依赖管理
- 单元测试
- 项目实战
项目地址为(GitHub - Moonlight-Zhao/go-project-example at V0)
开发环境使用windows10/11 + WSL2, go编辑器为 VSCode + go 插件。vscode可以使用remote-wsl插件连接WSL。
并发编程
并发 vs 并行
两者是不是一回事,这个问题取决于讨论的领域。就操作系统这方面而言,
- 并发指多线程程序在一个核的CPU上运行;
- 并行指多线程程序在多个核的CPU上运行。
go通过GMP模型,有实现高并发的能力。
goroutine
go 实现的协程, 协程:用户态,轻量级。 协程切换比线程切换快主要有两点:
- 协程切换完全在用户空间进行,线程切换涉及特权模式切换,需要在内核空间完成;
- 协程切换相比线程切换需要切换的上下文较少。基本只涉及CPU上下文,少量的栈空间
协程间通信
go提倡使用通信来共享内存,而不是共享内存实现通信
go提供的方案有以下两种:
- channel
- Sync(lock)
其它,协程等待:WaitGroup。
依赖管理
背景
go的项目依赖管理主要经历了以下三个阶段:
- GOPATH
- GO Vendor
- GO MODULE
- GOPATH方式的主要缺陷:当不同的项目依赖不同的包版本时,无法很好的处理。这是因为全局共用相同的包文件。
- GO Vendor方式的主要缺陷:没有很好地考虑依赖链,例如A<-B_V1,A<-C, C<-B_V2,那么此时A的Vendor下应该放哪个版本的B呢?可能会有不兼容。
依赖管理三要素
- 配置文件,描述依赖:go.mod
- 中心仓库管理依赖库:proxy
- 本地工具: go get/mod
go.mod
对于如何写go.mod,可自行查阅相关资料。 应当了解以下知识点:
- 由哪几部分组成:依赖管理基本单元、原生库、单元依赖
- 依赖配置项构成:包地址+version(语义化版本/基于commit的伪版本)
- 依赖配置项的附加关键词:indirect、incompatible
依赖图
下面是个小例子
proxy
增加一个依赖包代理,主要原因和目的如下:
具体配置如下
工具
自行查阅 go get,go mod 两个命令的使用。
测试
分类
- 回归测试(回归到场景中使用测试)
- 集成测试(对服务暴露的接口进行自动化集成测试)
- 单元测试(对各个函数功能进行测试) 从上到下覆盖率上升
单元测试
流程:输入->测试单元->输出->校对 作用:保证质量,提升效率(更正定位问题)
对于测试中比较结果是否正确,推荐使用包 github.com/stretchr/testify/assert
mock测试
对于单元测试的依赖,一般有下面两个要求,
幂等:重复运行一个测试,结果应该一样
稳定:单元测试能在任何时间稳定运行
因此需要使用mock,来模拟读写文件、缓存、数据库等。
推荐一个用于快速mock的包 bouk/monkey: Monkey patching in Go (github.com)
基准测试
略
项目实战
一个项目的开发流程是:需求设计->代码开发->测试
前置技能
-
web 框架:Gin - github.com/gin-gonic/g…
- 了解go web框架的简单使用
-
分层结构设计:github.com/bxcodec/go-…
- 了解分层设计的概念
项目背景和需求
- 实现一个展示话题(标题,文字描述)和回帖列表的后端 http 接口;
- 本地文件存储数据
项目地址为Moonlight-Zhao/go-project-example at V0 (github.com)
具体设计
本项目使用三层结构:数据层-逻辑层-视图层,即Repository-Service-Controller
- 数据层:画出话题和帖子的ER关系图,设计Topic 和 Post 两个结构体和其方法。设计一个索引机制,相关函数有根据文件初始化索引、查询等。设计一个实体结构体(PageInfo),其包含一个Topic,和一个PostList。
- 在逻辑层,考虑一个服务的流程是:参数校验、准备数据、组装实体,一一实现,其中可以使用go协程来提高并发。
- 在视图层,主要就是构建View对象(PageData),判断是否填入业务错误码。
- 路由,即开启网络服务,可以借助Gin配置。 整个Web服务的启动流程就是:初始化数据索引、初始化引擎配置、构建路由、启动服务。