GO语言入门之工程实践 | 青训营笔记

135 阅读5分钟

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

语言进阶

并发编程

Go 中实现并发安全的API有 Lock 锁,WaitGroup,条件变量 Condition (即 wait-notify 机制),通道 Channel 等

并发和并行

并发,指的是多线程程序在 一个核的 CPU 上 运行

并行,指的是多线程程序在 多个核的 CPU 上 运行

Goruntine

Go 实现高效并发的原因在于实现了自己的 协程。可以在用户态自己控制并发度,而Java 等语言的并发更多地依赖系统的调度

线程是运行在内核态的,我们说它的创建,销毁等操作都是重量级的;线程的空间是 MB 级别的;

而协程是运行在用户态的,可以称之为轻量级线程,其空间是 KB 级别的

创建 Goruntine 的方式就是在调用函数的函数名前加一个 Go 关键字,这个函数就会被新创建一个协程然后运行

CSP (Communicating Sequential Processes)

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

CSP

Channel

“通过通信来共享内存” 的实现就需要通道的支持。可以通过 make(chan 元素类型[,缓冲大小]) 来创建一个通道,其中缓冲大小的指定是可选的,因此又可以分为有缓冲通道和无缓冲通道

无缓冲通道会导致发送的 Goroutine 跟接收的 Goroutine 同步化,因此无缓冲通道又被称为同步通道。因为 Channel 与队列不同,它更偏向于一种 同步 通信机制,不是说把我要发送的东西发到 Channel 中我就可以去完成其它事情,而是需要等待 Channel 的另一端有其它线程把数据取走了我才能离开,即发送动作会阻塞等待另一端的接收动作。当然,如果 Channel 中没有数据,接收动作也会阻塞

而有缓冲通道的话,由于有缓冲区提供一定的缓冲,数据发送与接收可以异步执行。不过,当缓冲区也被放满了数据,它就跟无缓冲通道一样了,需要同步

并发安全 Lock

虽然 Go 提倡使用通道来共享数据,但其也保留了通过共享内存来实现通信的机制,这种机制下自然就会出现临界资源并发不安全的问题。可以使用 Sync 包下的关键字如 Lock,waitGroup 来实现并发安全操作和协程之间的同步

go 提供了竞争 race 的检测工具,只需在命令行使用 go 命令运行代码时加上 -race 选项即可

WaitGroup

使用 time.Sleep 来实现协程阻塞的方式是不优雅的,可以使用 WaitGroup 来实现

依赖管理

简单的小项目可以完全使用语言自带的 API 进行编码,而复杂的工程项目是不可能基于标准库搭建的,需要借助第三方的组件,依赖。那么在项目中对依赖的管理就非常重要 Go 的依赖管理演进经过了三个阶段:GOPATH -> Go Vendor -> Go Module

GOPATH

GOPATH 分为三部分,bin,pkg,以及 src。项目源码都放在 src 中,包括使用的依赖,然后会使用 go get 更新最新版本。弊端就在于无法实现依赖的多版本控制

Go Vendor

在项目目录下增加 vendor 文件,所有依赖包符本形式放在 $ProjectRoot/vendor 下,依赖的寻找方式就是先找 vendor,再找 GOPATH

Go Module

通过 go.mod 文件管理依赖包版本;通过 go get/go mod 指令工具管理依赖包

依赖管理三要素

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

依赖分发

直接从远程仓库如 GitHub,SVN等代码托管平台获取代码无法保证可靠性和稳定性,而且会对这些平台增加压力。因此引入了 Proxy,即环境变量中的 GOPROXY

测试

没有经过测试的工程可能会出现很多的事故,因此测试是非常重要的。 测试包括 回归测试,集成测试,单元测试。从上到下覆盖率增大而成本降低

单元测试覆盖率就是反映测试水准的数据。覆盖率的计算就是看被测试函数中的代码有多少行被执行在总行数中的占比

基准测试 常用于测试代码的执行性能

Mock

对于有依赖的函数功能的测试,如测试某个处理文件的函数,这个函数的测试结果会依赖于文件,那么当文件被删除,或发生变化等,就会导致测试结果不同。因此对于这种测试,引入了 Mock,对被测函数进行 打桩,使其不依赖于本地文件

QA

  1. 关于登陆使用 session + cookie 还是 JWT 的方式 session + cookie 可能涉及到后台的解密或数据查询等操作,然后 JWT 不太需要存储而是服务端直接解密;一般和客户端上交互是 session + cookie 进行鉴权;然后服务端之间通过 HTTP 交互的话通过 JWT 鉴权