Go 语言进阶 - 工程进阶
Go 语言进阶 - 工程进阶
Go 语言进阶 - 工程进阶
概述
本节课程主要分为四个方面:
- 并发编程
- 依赖管理
- 单元测试
- 项目实战
详述
- 罗列课程中涉及到的概念和相关资料,对于不熟悉的知识点,希望同学们可以提前查询预习,届时跟上直播课程进度。
- 【必须】课程内容相关代码链接:github.com/Moonlight-Z…
并发编程
- 协程Goroutine
- 通道Channel
- 锁Lock pkg.go.dev/sync
- 线程同步WaitGroup pkg.go.dev/sync
属于编程进阶内容,考虑到工程项目的可用性和可靠性,工程实践中经常会用到。
依赖管理
- Gopath
- Go Vendor
- Go Module : go.dev/blog/using-…
了解Go依赖管理演进的历程,通过课程学习以及课后实践能能够熟练使用go module 管理依赖。
单元测试
- 单元测试概念和规则:go.dev/doc/tutoria…
- Mock测试:github.com/bouk/monkey
- 基准测试:pkg.go.dev/testing#hdr…
项目实战
需求模型来源
青训营话题页forum.juejin.cn/youthcamp/p…
需求
- 实现一个展示话题(标题,文字描述)和回帖列表的后端http接口;
- 本地文件存储数据
组件及技术点
- web框架:Gin - github.com/gin-gonic/g…
- 了解go web框架的简单使用
- 分层结构设计:github.com/bxcodec/go-…
- 了解分层设计的概念
- 文件操作:读文件pkg.go.dev/io
- 数据查询:索引www.baike.com/wikiid/5527…
课程笔记
课程链接:
- https://juejin.cn/course/bytetech/7140987981803814919/section/7141271987174768676
- https://juejin.cn/course/bytetech/7140987981803814919/section/7141273296397402148
语言进阶
Go可以充分发挥多核的优势,高效运行
线程:内核态,比较重量级
协程:用户态,线程可以跑多个协程,比较轻量
Goroutine
快速打印:
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)
}
最后是使用time.sleep进行阻塞,防止在协程未运行结束前主线程先运行结束了。
Channel
协程通过通信来共享内存
func CalSquare() {
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)
}
}
锁
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("WithoutLock:", x)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
println("WithLock:", x)
}
WaitGroup并发同步
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()
}
依赖管理
GOPATH:环境变量,项目代码直接依赖src下的代码,go get下载最新的包到src目录下
Go Vendor:增加vendor文件,存放依赖包的副本,优先从vendor文件里面查找,但是仍然无法控制依赖的版本
Go Module:go.mod:依赖管理基本单元、原生库、单元依赖
测试
单元测试
- 所有测试文件以_test.go结尾
- func TestXxx(*testing.T)
- 初始化逻辑放到TestMain中
func HelloTom() string {
return "Tom"
}
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
assert.Equal(t, expectOutput, output)
}
添加–cover参数可以评价测试代码的覆盖率
Mock测试
一些函数对本地的数据库、文件等有强依赖,在测试的同时找到这些依赖要求过高
可以使用Mock进行测试,在函数执行的时候替换成另外一个函数(打桩),从而规避掉对本地其他的强依赖
func ReadFirstLine() string {
open, err := os.Open("log")
defer open.Close()
if err != nil {
return ""
}
scanner := bufio.NewScanner(open)
for scanner.Scan() {
return scanner.Text()
}
return ""
}
func ProcessFirstLine() string {
line := ReadFirstLine()
destLine := strings.ReplaceAll(line, "11", "00")
return destLine
}
func TestProcessFirstLine(t *testing.T) {
firstLine := ProcessFirstLine()
assert.Equal(t, "line00", firstLine)
}
func TestProcessFirstLineWithMock(t *testing.T) {
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
defer monkey.Unpatch(ReadFirstLine)
line := ProcessFirstLine()
assert.Equal(t, "line000", line)
}
基准测试
对函数的运行时间进行测试:go test -bench=.
var ServerIndex [10]int
func InitServerIndex() {
for i := 0; i < 10; i++ {
ServerIndex[i] = i+100
}
}
func Select() int {
return ServerIndex[rand.Intn(10)]
}
func FastSelect() int {
return ServerIndex[fastrand.Intn(10)]
}
func BenchmarkSelect(b *testing.B) {
InitServerIndex()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Select()
}
}
func BenchmarkSelectParallel(b *testing.B) {
InitServerIndex()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Select()
}
})
}
func BenchmarkFastSelectParallel(b *testing.B) {
InitServerIndex()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
FastSelect()
}
})
}
项目实战:社区话题页面
需求
- 实现一个展示话题(标题,文字描述)和回帖列表的后端http接口;
- 本地文件存储数据
分层结构
- 数据层:数据Model,处理外部数据的增删改查
- 逻辑层:业务Entity,处理核心业务逻辑输出
- 视图层:视图View,处理和外部的交互逻辑
组件及技术点
- web框架:Gin - github.com/gin-gonic/g…
- 了解go web框架的简单使用
- 分层结构设计:github.com/bxcodec/go-…
- 了解分层设计的概念
- 文件操作:读文件pkg.go.dev/io
- 数据查询:索引
具体逻辑见代码
课后实践
- 支持对话题发布回帖。
- 回帖id生成需要保证不重复、唯一性。
- 新加回帖追加到本地文件,同时需要更新索引,注意Map的并发安全问题 。
Go 语言进阶 - 工程进阶
https://zhangzhao219.github.io/2023/01/16/ByteDanceYouthTrainCamp/ByteDanceYouthTrainCamp-Day02/