这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
Go高并发的本质
1.0 并发和并行
并发:处理器单核利用上下文切换快速切换多个线程任务。
并行: 处理器多核同时运行多个任务。
1.1 Goroutine(协程)
线程:线程是较重的系统资源,相关操作由系统完成。
协程:协程相关操作由Go语言本身完成。
协程比线程更加轻量级,这就是Go语言更加适合高并发编程的原因
1.2 CSP(Communicating Sequential Pricesses)
通过共享内存实现通信: 该方式可能会出现竞态条件而影响性能。
PS:当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。
package main
import (
"fmt"
"time"
)
func hello(i int) {
println("hello goroutine : " + fmt.Sprint(i))
}
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}
func main() {
HelloGoRoutine()
}
1.3 Channel
Channel 是一种引用类型
package learn
func CalSquqre() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
// 复杂操作
println(i)
}
}
1.4 并发安全 Lock
并发安全是十分重要的一点,在并发中出现的问题较多且难排查,所以要避免在未设定高并发的条件下使用高并发。
1.5 WaitGroup
package learn
import "sync"
func ManyGoWait() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
依赖管理
2.1 Go 依赖管理演进
2.1.1 GOPATH
GOPATH指定开发人员的工作环境
弊端:无法实现package的多版本控制。
2.1.2 GO Vendor
GO Vendor的引进解决了项目不能导入package不同版本的问题
弊端:
- 无法控制依赖的版本。
- 更新项目又可能会出现依赖冲突,导致编译出错
2.1.3 GO Module
- 通过
go.mod文件管理依赖包版本 - 通过
go get/go mod指令工具管理依赖包
2.2 依赖管理三要素
- 配置文件,描述依赖
go.mod - 中心仓库管理依赖库
Proxy - 本地工具
go get和go mod
2.3 依赖配置
2.3.1 依赖配置- go.mod
module github.com/wangkechun/go-by-example
go 1.18
require(
github.com/wangkechun/go-by-example/learn v1.0.0
)
2.3.2 依赖配置- version
语义化版本
${MAJOR}.${MINOR}.${PATCH}
MAJOR:大版本,可以不兼容(代码隔离)
MINOP:代码兼容
PATCH:bug修改
基于commit 伪版本
vx.0.0-yyyymmddhhmmss-abcdefgh1234
2.3.3 依赖配置- indirect
2.3.4 依赖配置- incompatible
PS:主版本2+:版本号在v2以上
2.3.5 依赖配置-依赖图
Go版本选择算法会选择最低的兼容版本
2.3.6 依赖分发-回源
存在问题:
- 无法保证构建稳定性:原作者可以增加、修改、删除软件版本
- 无法保证依赖可用性:原作者可以删除软件
- 增加第三方压力:代码托管平台负载问题
2.3.7 依赖分发-Proxy
建立一个中间站,解决回源存在的问题--适配器模式
2.3.8 依赖分发-变量 GOPROXY
GOPROXY=proxy1.cn,proxy2.cn,direct
2.3.9 工具- go get
2.3.10 工具 -go mod
测试
3.0 事故
3.1 测试
- 回归测试:手动回归一些主流场景进行测试
- 集成测试:对系统功能维度进行测试,自动化回归测试
- 单元测试:面对测试开发阶段,开发者对单个接口进行测试
3.2 单元测试
通过单元测试保证质量和提升效率
3.2.0 单元测试-规则
package main
import (
"os"
"testing"
)
func TestLearnTest(t *testing.T) {
println("xixi")
}
func TestMain(m *testing.M) {
// 测试前:数据装载、配置初始化等前置工作
code := m.Run()
// 测试后:释放资源等收尾工作
os.Exit(code)
}