Go语言上手—应用实践 | 青训营笔记

158 阅读1分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记

01 语言进阶

并发编程

01 并发VS并行

并发:多线程程序在一个核的cpu上运行

并行:多线程程序在多个核的cpu上运行

image-20220508172735096

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

1.1Goroutine(轻量级线程)

协程:用户态,轻量级线程,栈MB级别。

线程:内核态,线程跑多个协议,栈KB级别。(比较消耗资源)

//打印hello goroutine:0~hello goroutine:4
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)
}
1.2CSP(Communicating Sequential Processes)

提倡通过通信共享内存而不是通过共享内存而实现通信

image-20220508205446795

通过共享内存而实现通信在一定程度上影响性能

make(chan 元素类型,[缓冲大小])

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

image-20220508205930723

无缓冲通道(同步通道)会导致发送的Goroutine和接收的Goroutine同步化,解决同步问题的方式就是用有缓冲区的有缓冲通道(生产消费模型)。

1.4并发安全Lock
  • 如果多个Goroutine在没有相互同步的情况下,访问某个共享的资源,并试图同时读和写这个资源,就处于相互竞争的状态,这种情况被称作竞争状态。
  • 并发安全问题有一定概率引发错误事件,比较难定位,应避免对共享内存做非并发安全的读写操作。
1.4WaitGroup

计数器

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

//快速打印hello goroutine:0~hello goroutine:4
func ManyGoWait() {
    var wg sync.WaitGroup
    wq.Add(5)
    for i := 0; i < 5; i++ {
        go func(j int) {
            defer wg.Done()
            hello(j)
        }(i)
    }
    wg.Wait()
}

02 依赖管理

背景

  • 工程项目不可能基于标准库0-1编码搭建
  • 管理依赖库

Go依赖管理演进

GOPATH

弊端:无法实现package的多版本控制

Go Vender通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。

Go Vender

弊端:无法控制依赖的版本;更新项目又可能出现依赖冲突,导致编译出错。

Go Module解决无法控制依赖版本的问题

Go Module

定义版本规则和管理项目依赖关系

依赖管理三要素

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

依赖配置

最终编译时会选择最低的兼容版本

依赖分发——回源

直接用github等代码托管系统平台使用版本管理仓库下载依赖存在多个问题:无法保障构建确定性,依赖可用性,增加第三方托管平台压力。

因此可以用Go Proxy(服务站点)

工具——go get

image-20220508221504217

工具——go mod

image-20220508221538733

03 测试

测试分为三种类型:

回归测试(手动通过终端回归一些固定的主流程场景/修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误)

集成测试(自动化的回归测试/在单元测试的基础上,将所有模块按照设计要求组装成为子系统或系统,进行集成测试)

单元测试(开发者对单独模块测试/保证质量)

从上到下,覆盖率逐层变大,成本却逐层降低

单元测试——规则

  • 所有测试文件以_test.go结尾
  • func TestXxx(*testing.T)
  • 初始化逻辑放到TestMain中,测试前:数据装载、配置初始化等等前置工作;测试后,释放资源等收尾工作。

单元测试——覆盖率

--cover测试代码覆盖率

单元测试——依赖

外部依赖 >= 稳定&幂等

幂等:重复运行,结果是一样的。

稳定:相互隔离,能在任何时间,任何环境,运行测试。

实现这一目的要用到mock机制。

单元测试——Mock

mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。

Monkey:一个开源的mock测试库,可以对method,或实例的方法进行mock,反射,指针赋值。

github.com/bouk/monkey

快速Mock函数

  • 为一个函数打桩
  • 为一个方法打桩

基准测试

  • 基准测试是指测试一段程序的运行性能及耗费CPU的程度。
  • 在开发中,会遇到代码性能瓶颈,为了定位问题经常要对代码做性能分析,这就用到了基准测试。
  • 使用方法类似于单元测试