这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
并发和并行
- 并发:多线程程序在一个核的 cpu 上运行(轮流执行)
- 并行:多线程程序在多个核心的 cpu 上运行(同步进行),可以理解为实现并发的一个手段
- Goroutine
- 协程:用户态,轻量级线程,栈 KB 级别
- 创建一个协程:在调用函数的时候,在函数的前面加上一个 go 关键字
- 线程:内核态,线程跑多个协程,栈 ,MB 级别
- 创建、切换、停止都属于很重的系统操作,占用是兆级别
- CSP:提倡通过通信共享内存而不是通过共享内存实现通信
- 通信共享内存
- 共享内存实现通信
影响程序的性能
- 通信共享内存
- 协程:用户态,轻量级线程,栈 KB 级别
- channel
make(chan 元素类型,[缓冲大小])- 无缓冲通道(同步通道),发送和接收同步
- 有缓冲通道,需要有人生产和消费,如果没人消费(缓冲区填满了)那么就会阻塞生产。因此当消费比生产慢的时候,可以考虑使用有缓冲通道进行同步
- 并发安全
- 可以使用 sync.Mutex 获取到锁
- 在执行需要考虑并发安全的函数的时候,可以考虑使用sync.Mutex.Lock()为其加锁,在函数执行结束后,使用 sync.Mutex.Unlock()为其解锁
- WaitGroup
- Add(delta int) 表示给计数器加上delta的数量
- Done() 表示计数器-1
- Wait() 表示会一直阻塞结束后续的进行直到计数器为 0
依赖管理
-
GOPATH
- 项目代码会直接依赖于 src 下的代码
- 导致的弊端:无法实现 package 的多版本控制,由于都是依赖于同一个 src 文件夹下的源码,可能会导致过去版本的一些函数由于升级被删除或修改
-
Go Vendor
- 项目目录下增加 vendor 文件夹,所有依赖包副本形式放在 $Project/vendor
- 寻址方式为:vendor=>GOPATH
- 此时每个项目引入一份依赖的副本
- 弊端:如果依赖之间互相不兼容,那么就可能导致依赖冲突进而导致编译出错
-
Go Module
- 通过 go.mod 文件管理依赖包版本
- 通过 go get/go mod 指令工具管理依赖包
-
依赖管理三要素
- 配置文件,描述依赖:go.mod
- 依赖标识:[Module Path][Version/Pseudo-version]
- 语义化版本:${MAJOR}.${MINOR}.${PATCH}
- MAJOR 表示大版本,不同的 MAJOR 间版本是隔离的
- MINOR 表示新增功能,MINOR 彼此之间代码是可以兼容的
- PATCH 负责代码块的修复
- 基于 commit 伪版本:版本号-时间戳(yyyymmddhhmmss)-hashcode
- indirect 表示非直接依赖
- incompatible 表示可能存在不兼容的代码
- 在选择版本的时候,会选择可以兼容的最低版本进行编译
- 中心仓库管理依赖库:Proxy
- 使用版本管理工具下载依赖存在的问题
- 无法保证构建稳定性
- 无法保证依赖可用性
- 增加第三方压力
- Proxy 可以保证稳定和可靠
- 使用版本管理工具下载依赖存在的问题
- 本地工具:go get/mod
- 配置文件,描述依赖:go.mod
测试
- 回归测试
- 通过终端回归一些特定的场景测试
- 集成测试
- 功能的测试
- 单元测试
- 开发者对函数的单个模块进行测试
- 一般覆盖率50%-60%,较高覆盖率80%+
- 测试分支相互独立全面覆盖
- 测试单元粒度足够小
- 函数单一职责
- MOCK:
- 幂等:重复测试结果应该是一样的
- 稳定:单元测试之间相互隔离
- 打桩测试:使得不需要对文件产生强依赖,打桩的主要原理是通过替换原本的测试集,使得需要测试的函数
- 所有测试文件以 _test.go结尾,函数以 func TestXXX(*testing.T) 作为方法签名,初始化逻辑放到 TestMain 中