并发 VS 并行
-
并发:多线程程序在一个核的cpu上运行
并行:多线程程序在多个核的cpu上运行 (实现并发的一个手段)
-
Go可以充分发挥多核优势,高效运行
协程 VS 线程
-
线程:内核态,线程跑多个协程,创建、切换、停止属于很重的系统操作,栈MB级别 (可以创建上万点协程)
-
协程:用户态,轻量级线程,栈KB级别
-
协程之间通信:go提倡通过通信共享内存
channel:从一个goroutine发送数据到另一个goroutine的数据传输机制。遵循先入先出,保证数据收发到顺序
-
通过共享内存实现通信:需要获取临界区的权限(加锁),可能会影响性能(go也可以)
Channel
使用 make(chan 元素类型,[缓冲大小])
- channel可以为无缓冲通道或有缓冲通道
- 无缓冲通道:也被称为同步通道
- 有缓冲通道:通道大小代表通道中能存放多少元素,超出会阻塞发送
- Why using defer close()
并发安全Lock
- 使用 lock sync.Mutex, lock.Lock() 和 lock.Unlock()进行获取和释放临界区权限
WaitGroup
-
子协程确切的执行时间未知 ➡️ 无法设置准确的sleep时间
-
在go中可以使用WaitGroup来实现并发任务的同步,内部维护一个计数器
Add(delta int):计数器+delta
Done():计数器 - 1
Wait():阻塞直到计数器为0
-
开启协程 + 1
执行结束 - 1
主协程阻塞知道计数器为0
依赖管理
-
Go Module现在广为应用
-
不同环境依赖的版本不同,控制以来库的版本
-
GOPATH是环境变量,go项目的工作区
bin:项目编译的二进制文件
pkg:项目编译的中间产物,加速编译
src:项目源码
项目代码直接依赖src下的代码,通过go get下载最新版本的包到src目录下
弊端:A和B依赖于某一package的不同版本,无法实现package的多版本控制。
-
GO Vendor
项目目录下增加vendor文件,所有依赖包以副本形式放在$ProjectRoot/vendor,解决了多个项目需要同一个package依赖冲突的问题。
依赖寻址: vendor ⇒ GOPATH
弊端:无法控制依赖的版本,更新项目有可能出现依赖冲突,导致编译出错。还是依赖源码,而非依赖版本。
-
Go Module
通过go.mod文件管理依赖包版本,通过go get/ go mod指令工具管理依赖包,实现定义版本规则和管理项目依赖关系
-
依赖管理三要素
- 配置文件,描述依赖 ⇒ go.mod
- 中心仓库管理依赖库 ⇒ Proxy
- 本地工具 ⇒ go get/mod
-
在编译时选择最低的兼容版本,v1.3/v1.4 ⇒ v1.4
单元测试
-
规则
- 所有测试文件以_test.go结尾
- func TestXxx(*testing.T)
- 初始化逻辑放到TestMain中,包括测试前的数据装载、配置初始化等前置工作,以及测试后释放资源等收尾工作
-
一般覆盖率50%~60%,较高80%
-
测试分支相互独立、全面覆盖
-
测试单元粒度足够小,函数单一职责。
-
依赖
- 单元测试会依赖如File, DB, Cache等外部依赖。
- 幂等:重复运行同一个测试结果都相同
-
Mock机制
- monkey:mock工具
- 可以实现不依赖本地文件
基准测试
- 优化代码,需要对当前代码分析
- 内置的测试框架提供了基准测试的能力
分层结构模型
- 数据层:数据Model,外部数据的增删改查
- 逻辑层:业务Entity,处理核心业务逻辑输出
- 视图层:视图view,处理和外部的交互逻辑