2 - Go 并发 依赖 测试 | 青训营笔记

122 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

老师的项目地址

本堂课重点内容

  • 并发事件及其相关处理
  • 依赖管理
  • 测试
  • 完整的项目实践

详细知识点介绍

1. 语言进阶

Goroutine - 协程的概念

首先是并发(图左)与并行(图右)

image.png

Go 可以说就是为了高并发设计的

然后是 协成线程

image.png

协程由 Go 管理,是更轻量的线程

协程例子

如何 快速 打印 hello goroutine : 0 ~ hello goroutine :4

package concurrence

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) // 只是为了保证子协程完成前主协程不退出
}

CSP - 协程间的通信

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

image.png

Channel

image.png

代码 ./concurrence/channerl.go 为 channel 的例子。因为生产者(产生数字)比消费者(使用数字)效率可能更高,故使用带缓冲区的 channel 保证顺序性(并发安全性)

:Go 语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行

Sync

共享内存时的并发安全

image.png

加锁!

WaitGroup

刚刚的例子都用了 sleep 实现暴力堵塞。这是更优雅的实现,,相当于内部开了计时器,阻塞直到计数器为 0

// 刚刚的函数
func HelloGoRoutine() {
	for i := 0; i < 5; i++ {
		go func(j int) { // 为了快速,多协程
			hello(j)
		}(i)
	}
	time.Sleep(time.Second) // 只是为了保证子协程完成前主协程不退出
}
// 使用wg
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. 依赖管理

管理方法

简单来说就是我们不想重复造轮子,但是引入不同的包有可能依赖路径不同,也就催生了依赖管理

可以参考

简单来说:

  • GOPATH 没法解决 A 和 B 两项目依赖某一 package 的多个版本的情况
  • Go Vendor 每个项目建 package 的副本,但无法控制以来的版本,更新项目有可能出现冲突,导致出错
  • 所以有了 Go Module: 定义版本规则和管理项目依赖关系

Go module 其实类似 Java 里的 maven,三要素为:

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

一个还挺神奇的例子:

image.png

依赖分发

其实就是依赖去哪里下载、如何下载

直接从分发平台(如 Github)拉取的话,可能不稳定(对方随时增删改版本)、无法保证可用、增加了对第三方平台的压力。为了解决这个问题,有了 Proxy ,它缓存某个版本,实现稳定依赖分发

Go mod 通过 GOPROXY 环境变量配置 Proxy

image.png

3. 测试

  • 单元测试

    • 和 Java 的蛮像的,也有 assert 之类的
    • 基本使用可以看这里
    • 覆盖率的概念请参看下面的图,意思是前两行被验证过了,最后一个 return false 没有验证,需要再来个函数验证 image.png
    • 一些 tips:
      • 一般覆盖率五六成,较高覆盖率 80%+
      • 测试分支相互独立、全面覆盖
      • 测试单元力度足够小,函数单一职责
    • 需要 幂等(重复测试结果一样),稳定(单元测试相互隔离,在任何时间对任何行运行)
    • 数据库和文件的测试不一定那么方便直接,可能有被人修改等问题,可以使用 MOCK 方法,将内存中函数地址替换,不再依赖数据库/文件
    • Go 提供了 benchmark 测试框架,可以测试运行时间等
  • 集成测试

  • 回归测试

项目实践

需求描述如下

image.png

  • 话题 Topic
  • 帖子 PostList

从头实现了一个这样的项目(包括测试),还蛮有意思的。觉得值得一记的地方:

  • 相比直接遍历,不如全部存到 map 再直接通过索引拿到内容
  • 单例模式
  • 对于不相互依赖的流程,考虑并行执行提高效率

课后个人总结

真的很干很硬,课后真的得重新运行实现;反过来说也真的学到了很多,很少见到这么完整的 demo 代码