语言进阶
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)
data := 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的并发安全问题 。