Go第二节课 | 青训营笔记

98 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第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 依赖管理三要素

  1. 配置文件,描述依赖 go.mod
  2. 中心仓库管理依赖库 Proxy
  3. 本地工具 go getgo 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)
}

3.2.1 单元测试-例子