这是我参与「第三届青训营 -后端场」笔记创作活动的的第 2 篇笔记。
本篇笔记对第二节课《Go语言上手 - 工程实践》的知识点进行汇总。本节课的内容主要分为并发编程、依赖管理、单元测试、项目实战四个部分。
并发编程
并发与并行
- 并发:指在同一时刻只执行一条指令执行。多个进程的情况下,把时间分为若干端,指令被快速的轮换执行,宏观上具有多个进程同时执行的效果。
- 并行:指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。
进程、线程与协程
- 进程:进程是程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的独立单位,是应用程序运行的载体。
- 线程:线程是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,各个线程之间共享进程的内存空间。
- 协程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制 栈为KB级别。
CSP(Communicating Sequential Processes)
协程在Go语言中是通过通信共享内存,使用goroutine和channel实现的CSP并发模型。 channel分为无缓冲和有缓冲。创建channel的代码为:
make(chan int {, 2}) // 花括号内表示有缓冲通道的创建
并发安全Lock
多个goroutine同时访问某一块内存,可能会存在并发安全的问题。
使用等待组WaitGroup进行多个任务的同步,保证在并发环境下的指定任务数量的完成。WaitGroup的常用几个函数如下:
| 方法名 | 功能 |
|---|---|
| (wg * WaitGroup) Add(delta int) | 等待组的计数器 +1 |
| (wg * WaitGroup) Done() | 等待组的计数器 -1 |
| (wg * WaitGroup) Wait() | 当等待组计数器不等于 0 时阻塞直到变 0。 |
依赖管理
依赖管理的演化过程: GoPath → Go Vendor → Go Module
GoPath
- 环境变量 $GOPATH
bin //项目编译的二进制文件
pkg // 项目编译的中间产物,加速编译
src //项目源码
- 项目代码直接依赖src下的代码
- go get 下载最新版本的包到src目录下
弊端:无法实现package的多版本控制
Go Vendor
- 项目目录下增加vendor 文件,所有依赖包副本形式放在$ProjectRoot/vendor。
- 依赖寻址方式:vendor -> GoPATH。
通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。 弊端:无法控制依赖的版本;更新项目又可能出现依赖冲突,导致编译出错。
Go Module
- 通过go.mod 文件管理依赖包版本
- 通过 go get/ go mod 指令工具管理依赖包
依赖管理的三要素:
- go.mod:配置文件,描述依赖
- Proxy:中心仓库管理依赖库
- go get/mod:本地工具
依赖图:
测试
单元测试
单元测试规则:
- 测试文件均以“_test.go”结尾;
- 测试函数命名“func TestXxx(t *testing.T)”
- 初始化逻辑放在TestMain中
func TestMain(m *testing.M){
//测试前:数据装载、配置初始化等前置工作
code := m.Run()
// 测试后:释放资源等收尾工作
os.Exit(code)
}
Tips:
- 一般覆盖率:50~60%,较高覆盖率80%+
- 测试分支相对独立、全面覆盖
- 测试单元粒度足够小,函数单一职责
基准测试
- 优化代码,需要对当前代码分析
- 内置的测试框架提供了基准测试的能力
项目实战
项目说明:社区话题页面
需求描述:
- 展示话题(标题,文字描述)和回帖列表
- 暂不考虑前端页面实现,仅仅实现一个本地web服务
- 话题和回帖数据用文件存储
需求用例: 用户与哪些对象进行交互
ER 图: 描述对象的属性及相互关系
分层结构:
- 数据层:数据 Model,外部数据的增删改查
- 逻辑层:业务 Entity,处理核心业务逻辑
- 视图层:视图 View,处理和外部的交互逻辑
组件工具:
- gin:高性能开源的 go web 框架,基于此搭建 web 服务器,主要使用到路由分发。
- Repository: 首先,定义业务对象结构体(Topic,Post),实现查询对象函数(QueryTopicById、QueryPostsByParentId)
- Service: 首先,定义 service 实体 其次流程,参数校验→准备数据→组装实体
- Controller: 构建 View 对象 业务错误码
- Router: 初始化数据索引,初始化引擎配置,构建路由,启动服务