Go 语言上手-工程实践 | 青训营笔记

174 阅读1分钟

Go 语言上手-工程实践 | 青训营笔记

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

1. 并发介绍

1.1 并发 VS 并行

并发: 多线程程序在一个核的cpu上运行

并行: 多线程程序在多个核的cpu上运行

Go可以充分发挥多核优势,高效运行

1.2 Goroutine

协程: 用户态, 轻量级线程, 栈MB级别

线程: 内核态, 线程跑多个协程, 栈KB级别

例:快速打印

func hello(i int) {
    println("Hello Goroutine!" + fmt.Sprint(i))
}
func main() {
​
    for i := 0; i < 10; i++ {
        go func(j int) {
            hello(j)
        }(i)
    }
    time.Sleep(time.Second)
}

1.3 CSP通信

提倡通过通信共享内存

1.4 Channel

make(chan 元素类型,[缓冲大小])

  • 无缓冲通道 make(chan int)
  • 有缓冲通道 make(chan int, 2)

当多个goroutine同时进行处理的时候,就会遇到同时抢占一个资源的情况(并发都会遇到的问题),所以我们希望某个goroutine等待另一个goroutine处理完某一个步骤之后才能继续。

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 { //通过src实现通信
            dest <- i * i
        }
    }()
    for i := range dest {
        //复杂操作
        println(i)
    }
}

desc 使用缓冲 channel:不会因为消费者的消费速度问题,影响生产者的执行 效率

1.5 WaitGroup

维护了一个计时器,实现了三个方法

Add : 计数器+delta

Done:计数器-1

wait:阻塞直到计数器为0

优化:快速打印

func hello(i int) {
    println("hello world : " + fmt.Sprint(i))
}
​
func ManyGo() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(j int) {
            defer wg.Done()
            hello(j)
        }(i)
    }
    wg.Wait() //进行阻塞
}

2. 依赖管理

2.1 Go依赖管理演进

GOPATH → Go Vendor → Go Module

  • 不同环境(项目)依赖的版本不同
  • 控制依赖库的版本

2.1.1 GOPATH

  • 环境变量$GOPATH

    • bin 项目编译的二进制文件
    • pkg 编译的中间产物 加速变异
    • src 项目源码
  • 项目代码直接依赖src下的代码

  • go get 下载最新版本的包到src目录下

弊端 : 无法实现多版本控制

2.1.2 Go Vendor

  • 项目目录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor
  • 依赖寻址方式: vendor → GOPATH

通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。

弊端:

  • 无法控制依赖的版本
  • 更新项目又可能出现依赖冲突,导致编译出错

2.1.3 Go Module

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

2.2 依赖管理三要素

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

2.3 依赖配置

2.3.1 依赖配置 - go.mod

//依赖管理基本单元
module github.com/Moonlight-Zhao/go-project-example
​
//原生库
go 1.16//单元依赖
require (
    github.com/gin-contrib/sse v0.1.0 // indirect
    github.com/gin-gonic/gin v1.3.0 // indirect

2.3.2 依赖配置 - version

语义化版本

${MAJOR}.${MINOR}.${PATH}

V1.3.0

V2.3.0

基于commit伪版本

vX.0.0-yyyymmddhhmmss-abcdefgh1234

2.3.3 依赖配置 - indirect

//indirect非直接依赖

2.3.4 依赖配置 - incompatible

  • 主版本2+模块会在模块路径增加/vN后缀
  • 对于没有go.mod文件并且主版本2+的依赖,会+incompatible

如果一个项目同时依赖一个项目多个版本 那么go.mod会选择最低兼容版本去依赖

2.3.5 依赖分发

回源
  • 无法保证构建稳定性
  • 无法保证依赖可用性
  • 增加第三方压力
Proxy

服务站点: 缓存软件内容,实现稳定可靠

2.3.6 依赖分发 - 变量 GOPROXY

配置 : 通过GOPRXY环境变量

2.3.7 工具 - go get

image-20220508120818278

2.3.8 工具 - go mod

image-20220508120902504

3. 测试

  • 回归测试
  • 集成测试
  • 单元测试

3.1 单元测试

image-20220508121227280

3.1.1 单元测试 -规则

  • 所有测试文件以 _test.go 结尾
  • func TestXxx(* testing.T)
  • 初始化逻辑放到 TestMain中

3.1.2 单元测试 - assert

assert.Equal*(t, one ,another)

3.1.3 单元测试 -代码覆盖率

go test xxx.go --cover

3.2 单元测试 - 依赖

外部依赖 → 幂等&稳定

3.3 单元测试 - Mock

monkey :github.com/bouk/monkey

快速Mock函数

  • 为一个函数打桩
  • 为一个方法打桩

Patch(target,replacement interface{}:

  • target : 原函数
  • replacement: 打桩函数

Unpatch(target interface{}): 测试结束后卸载桩

3.4 基准测试

Benchmarkxxx(b *testing.B)开头

b.ResetTimer时间重置

b.RunParallel并行

字节开源 FastRun: github.com/bytedance/g…

4. 项目实战

4.1 需求描述

社区话题页面

  • 展示话题(标题,文字)和回帖列表
  • 暂不考虑前端
  • 话题和回帖数据用文件存储

4.2 需求用例

image-20220508130905342

4.3 ER图

image-20220508131011633

4.4 分层结构

image-20220508131059371

4.5 组件工具

Gin 高性能go web框架

github.com/gin-gonic/g…

Go Mod

go mod init

go get gopkg.in/gin-gonic/gin.v1@v1.3.0

4.6 Repository

结构体:

  • Topic

    type Topic struct {
        Id         int64     `gorm:"column:id"`
        UserId     int64     `gorm:"column:user_id"`
        Title      string    `gorm:"column:title"`
        Content    string    `gorm:"column:content"`
        CreateTime time.Time `gorm:"column:create_time"`
    }
    
  • Post

    type Post struct {
        Id         int64     `gorm:"column:id"`
        ParentId   int64     `gorm:"parent_id"`
        UserId     int64     `gorm:"column:user_id"`
        Content    string    `gorm:"column:content"`
        DiggCount  int32     `gorm:"column:digg_count"`
        CreateTime time.Time `gorm:"column:create_time"`
    }
    
index

元数据 索引

数据行 → 内存Map

4.7 Service

参数校验 → 准备数据 → 组装实体

4.8 Controller

  • 构建View对象
  • 业务错误码

4.9 Router

  • 初始化数据索引
  • 初始化引擎配置
  • 构建路由
  • 启动服务

请求:

 curl localhost:8080/community/page/get/2

\