[Go语言进阶-工程进阶 | 青训营笔记]

90 阅读3分钟

[Go语言进阶-工程进阶 | 青训营笔记]

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

一、本课重点内容

  • 语言进阶
    • Goroutine
    • CSP
    • Channel
    • Lock
    • WaitGroup
  • 依赖管理
    • 背景
    • Go 依赖管理演进
    • Go Moudule 实践
  • 测试
    • 单元测试
    • Mock 测试
    • 基准测试
  • 项目实战
    • 需求设计
    • 代码开发
    • 测试运行

二、详细知识点介绍

2.1 并发 VS 并行

  • 并发:多线程程序在单核 CPU 上运行
  • 并行:多线程程序在多核 CPU 上运行

Go 可以充分发挥多核优势,高效运行。

2.1.1 Goroutine

  • 协程:用户态,轻量级线程,栈 MB 级别
  • 线程:内核态,线程跑多个协程,栈 KB 级别

函数前面加上 go 关键字,创建一个协程来执行。

2.1.2 CSP (Communicating Sequential Processes)

提倡使用通信共享内存而不是使用共享内存通信。

2.1.3 Channel

make (chan ElemType, [size])

  • 无缓冲通道 如:make (chan int)
  • 有缓冲通道 如:make (chan int, 2)

2.1.4 并发安全 Lock

生产者消费者等

2.1.5 WaitGroup

实现并发线程的同步

Add(delta int) 计数器加 delta Done() 计数器减1 Wait() 阻塞直到计数器为0

计数器:开启协程+1,执行结束-1,主协程阻塞直到计数器为0。

2.2 依赖管理

2.2.1 背景

  • 工程项目后不可能基于标准库0~1编码实现
  • 管理依赖库

Go 依赖管理演进:GOPATH \rightarrow Go Vender \rightarrow Go Moudule

GOPATH
  1. 环境变量 $GOPATH
  2. 项目代码直接依赖 src 下的代码
  3. go get 下载最新版本的包到 src 目录下
  4. 弊端:无法实现 package 的多版本控制
Go Vendor
  1. 依赖包副本形式放在 ProjectRoot/vendor
  2. 依赖寻址方式:vendor \rightarrow GOPATH
  3. 通过每个项目引入一份依赖的副本,解决了多个项目需要同一个 package 依赖的冲突问题
  4. 弊端:无法控制依赖的版本,更新项目又可能出现依赖冲突,导致编译出错
Go Moudule
  1. 通过 go.mod 文件管理依赖包版本
  2. 通过 go get/go mod 指令工具管理依赖包

2.2.2 依赖管理三要素

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

2.3 Go Moudule 实践

2.3.1 依赖配置 - go.mod

module example/project/app

go 1.16

require(
    example/lib1 v1.0.2
    example/lib2 v1.0.0
    example/lib3 v0.1.0-20190725025543-5a5fe074e612
    example/lib4 v3.2.0+incompatible
)

2.3.2 依赖配置 - version

语义化版本 ${MAJOR}.${MINOR}.${PATCH}

基于 commit 伪版本 vX.0.0-yyyymmddhhmmss-abcdefgh1234

2.3.3 依赖配置 - indirect

A \rightarrow B \rightarrow C:A \rightarrow B 是直接依赖,A \rightarrow C 是间接依赖。

2.3.4 依赖配置 - incompatible

主版本2+模块会在模块路径增加 /vN 后缀;对于没有 go.mod 文件并且主版本2+的依赖,会+incompatible。

2.3.5 依赖分发 - 回源

  • 无法保证构建稳定性(增加/删除/修改软件版本)
  • 无法保证依赖可用性(删除软件)
  • 增加第三方压力(代码托管平台负载问题)

2.3.6 依赖分发 - Proxy

GOPROXY="https://proxy1.cn, https://proxy2.cn, direct"

2.3.7 工具 - go get

go get example.org/pkg

  • @update 默认
  • @none 删除依赖
  • @v1.1.2 tag版本,语义版本
  • @23dfdd5 特定的 commit
  • @master 分支最新的 commit

2.3.8 工具 - go mod

  • init 初始化,创建 go.mod 文件
  • download 下载模块到本地缓存
  • tidy 增加需要的依赖,删除不需要的依赖

2.4 测试

2.4.1 单元测试 - 规则

  • 所有测试文件以 _test.go 结尾
  • FuncTestXxx(*testing.T)
  • 初始化逻辑放到 TestMain

2.4.2 单元测试 - 运行

go test [flags][packages]

2.4.3 单元测试 - 覆盖率

  • 一般覆盖率 50%~60%, 较高覆盖率 80%+
  • 测试分支相互独立、全面覆盖
  • 测试单元粒度足够小,函数单一职责

2.4.4 单元测试 - 依赖

外部依赖 \rightarrow 稳定&幂等

2.4.5 单元测试 - Mock

快速 Mock 函数:为一个函数/方法打桩

monkey: github.com/bunk/monkey

三、实践练习例子

3.1 需求描述

  • 展示话题和回帖列表
  • 暂不考虑前端页面实现,仅仅实现一个本地 web 服务
  • 话题和回帖数据用文件存储

3.2 分层结构

  • 数据层:数据 Model,外部数据的增删改查
  • 逻辑层:业务 Entity,处理核心业务逻辑输出
  • 视图层:视图 view,处理和外部的交互逻辑

(源代码见 github.com/Moonlight-Z…

四、课后个人总结

本课程主要学习了 Go 语言的进阶知识,包括并行与并发、协程、依赖管理和项目设计。