这是我参与「第五届青训营 」笔记创作活动的第2天
一、本堂课重点内容:
讲述了Go并发编程、工程依赖控制、项目从设计到编码的一个案例
二、详细知识点介绍:
1. Go并发
从并发编程的视角带大家了解 Go 高性能的本质
1.1. 并发 VS 并行
并发:多线程程序在一个核的CPU上运行
并行:多线程程序在多个核的CPU上运行
Go可以充分发挥多核优势,高效运行
协程:用户态,轻量级线程,栈MB级别
线程:内核态,线程跑多个协程,栈KB级别
package main
import (
"fmt"
"time"
)
func hello(i int) {
println("hello goroutine : " + fmt.Sprintf("%d", i))
}
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}
func main() {
HelloGoRoutine()
}
1.2 CSP
提倡通过通信共享内存而不是通过共享内存而实现通信
1.3 Channel
make(chan 元素类型, [缓冲大小])
package main
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
// A 协程发送 0-9的数字
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
// B 子协程计算输入数字的平方
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
// 主协程输出最后的平方数
for i := range dest {
println(i)
}
}
1.4 并发安全 Lock
package main
import (
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func Add() {
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
println("Without Lock: ", x)
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
println("With Lock: ", x)
}
func main() {
Add()
}
1.5 WaitGroup
计数器:开启协程 + 1;执行结束 -1;
主线程阻塞知道计数器为 0
package main
import "sync"
func hello(i int) {
println(i)
}
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()
}
func main() {
ManyGoWait()
}
2. 工程依赖管理
了解 Go 语言依赖管理机制
历史:GOPATH -> GO Vendor -> Go Module
不同环境(项目)依赖的版本不同 控制依赖库的版本
GOPATH:无法实现package的多版本控制 GO Vendor:无法控制依赖的版本,更新项目又可能出现冲突
GoModule:
- 通过 go.mod 文件管理依赖包版本
- 通过 go get/go mod 指令工具管理依赖包
终极目标:定义版本规则和管理项目依赖关系
依赖配置:{MINOR}.${PATCH}
indirect:直接引用
incompatible:可能会存在一些不兼容的代码逻辑
依赖分发:Proxy
go get ...
@update 默认、@none删除、@v1.1.2 tag版本、@23dfdd5 特定的commit @mater 特定的分支
go mod init 初始化 传教go.mod
go mod download 下载模块到本地缓存
go mod tidy 增加所需要的依赖,删除不需要的依赖
3. 单元测试
从单元测试实践出发,加强大家的质量意识
回归测试 -> 集成测试 -> 单元测试 从上到下,覆盖率逐层变大,成本逐层降低
要求:
-
所有测试文件以 _test.go结尾
-
func TestXXX(*testing.T)
-
初始化逻辑放在 TestMain 中
assert 断言:
import (
"github.com/stretchr/testify/assert"
)
assert.Equal(t, expectOutput, output)
一般覆盖率:50%-60%,较高覆盖率80%+
测试分支相互独立、全面覆盖
测试单元粒度足够小,函数单一职责
外部依赖 => 稳定 & 幂等
还有Mock测试、基准测试
4. 项目实战
通过项目需求、项目拆解、结构设计、代码实现带领大家感受下真实的项目开发
需求描述 -> 需求用例 -> 分层结构 -> ER图 -> 组件工具(技术选型)-> DAO层 -> Service层(参数校验、准备数据、组转实体 ) -> Controller 层(构建View对象、业务错误码)-> Router路由(初始化数据索引、初始化引擎配置、构建路由、启动服务)-> 运行