[ 后端与 Go | 青训营笔记 ]

77 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

课内:Go 工程实践

从工程实践角度,讲授在企业项目实际开发过程中的所遇的难题,重点讲解 Go 语言的进阶之路,以及在其依赖管理管理过程中如何演进。

重点

  • 语言进阶:从并发角度理解 Go 的高性能本质;
  • 依赖管理:了解 Go 语言依赖管理的演进路线;
  • 测试:单元实践;
  • 项目实战:理解真实项目开发。

细节

语言进阶

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

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

Goroutine 协程

image-20230116122931732

  • 协程较轻量,线程可以并发运行多个协程
  • 开启协程:快速打印,不考虑顺序
 func Hello() {
     for i := 0; i < 5;i++ {
         go func(j int) {// 使用go关键字开启协程
             fmt.Println(j)
         }(i) //传入i
     }
 }
CSP

提倡通过通信共享内存,而不是共享内存实现通信。

image-20230116171611627

Channel
  • Channel是Go中的一个核心类型,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。它的操作符是箭头 <-
 make (chan 元素类型,[缓冲大小])
 make(chan int) // 无缓冲通道
 make(chan int ,2) // 有缓冲通道
 ch <- v // 发送值v到Channel ch中
 v := <- ch // 从Channel ch中接收数据,并将数据赋值给v

image-20230116172104278

  • 它包括三种类型的定义。可选的 <- 代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据。
 chan T          // 可以接收和发送类型为 T 的数据
 chan<- float64  // 只可以用来发送 float64 类型的数据
 <-chan int      // 只可以用来接收 int 类型的数据
  • 使用 defer,延迟关闭 channel 。
  • Go语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。
  • 当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出),下面的代码是将一系列的数值打印语句按顺序延迟处理,如下所示
 package main
 ​
 import (
     "fmt"
 )
 ​
 func main() {
 ​
     fmt.Println("defer begin")
 ​
     // 将defer放入延迟调用栈
     defer fmt.Println(1)
 ​
     defer fmt.Println(2)
 ​
     // 最后一个放入, 位于栈顶, 最先调用
     defer fmt.Println(3)
 ​
     fmt.Println("defer end")
 }
 // 输出
 defer begin
 defer end
 3
 2
 1

image-20230116174132711

并发安全Lock

sync.Mutex是一个互斥锁,可以由不同的goroutine加锁和解锁。

  • 共享内存时,需要使用 Lock,保证每个协程不会冲突使用内存。sync.Mutex 是 Go 标准库提供的一个互斥锁,当一个 goroutine 获得互斥锁权限后,其他请求锁的 goroutine 会阻塞在 Lock() 方法的调用上,直到调用 Unlock() 方法被释放。
 var {
     x int 
     lock sync.Mutex// 互斥锁
 }

image-20230116180602411

WaitGroup

sync.WaitGroup 使用等待组进行多个任务的同步,等待组可以保证在并发环境中完成指定数量的任务

  • 在 sync.WaitGroup(等待组)类型中,每个 sync.WaitGroup 值在内部维护着一个计数,此计数的初始默认值为零。
  • 等待组方法
方法名功能
Add(delta int)等待组的计数器 +int
Done()等待组的计数器 -1
Wait()当等待组计数器不等于 0 时阻塞,直到变 0。
 package main
 ​
 import (
     "fmt"
     "net/http"
     "sync"
 )
 ​
 func main() {
 ​
     // 声明一个等待组
     var wg sync.WaitGroup
 ​
     // 准备一系列的网站地址
     var urls = []string{
         "http://www.github.com/",
         "https://www.qiniu.com/",
         "https://www.golangtc.com/",
     }
 ​
     // 遍历这些地址
     for _, url := range urls {
 ​
         // 每一个任务开始时, 将等待组增加1
         wg.Add(1)
 ​
         // 开启一个并发
         go func(url string) {
 ​
             // 使用defer, 表示函数完成时将等待组值减1
             defer wg.Done()
 ​
             // 使用http访问提供的地址
             _, err := http.Get(url)
 ​
             // 访问完成后, 打印地址和可能发生的错误
             fmt.Println(url, err)
 ​
             // 通过参数传递url地址
         }(url)
     }
 ​
     // 等待所有的任务完成
     wg.Wait()
 ​
     fmt.Println("over")
 }

image-20230116181948125

依赖管理

image-20230116182446789

GOPATH
  • bin:项目编译的二进制文件
  • pkg:项目编译的中间产物,加速编译
  • src:项目源码

image-20230116182834525

Go Vendor
  • 在项目目录下增加 Vendor ,引入依赖副本

image-20230116183033936

image-20230116183204377

Go Module
  • 通过 go.mod 文件管理依赖包版本
 module test/test_1/main // 依赖管理基本单元
 ​
 go 1.19
 ​
 require ( // 单元依赖
     // 依赖标识:[Module Path][Version/Pseudo-version]
 )

image-20230116183325609

image-20230116183424737

image-20230116183835380

image-20230116184019707

依赖配置 - indirect

对于 a -> b -> c

  • a -> b 为直接依赖
  • a -> c 为间接依赖(indirect)

image-20230116184355470

image-20230116184554219

image-20230116184851551

image-20230116184903977

image-20230116184944127

image-20230116185006044

测试

回归测试 -> 集成测试 -> 单元测试, 覆盖面增加,成本下降

单元测试
  • 提升效率,保证质量

  • 规则:

    所有测试文件以 _test.go 结尾,测试函数命名为 func TestXxx(*testing.T),初始化理解放到 TestMain

image-20230116211920377

image-20230116212122438

  • 覆盖率

测试数 | 测试水准 | 高水平测试

image-20230116212436688

  • 依赖

外部依赖 => 稳定 & 幂等

image-20230116212624662

  • Mock

为一个函数打桩,为一个方法打桩。

A 替换 B,B为原函数,A为打桩函数。

image-20230116212834255

基准测试

优化代码

实践

需求设计 | 代码开发 | 测试运行

需求设计

需求描述

image-20230116213534916

需求用例

image-20230116213720940

话题,帖子

ER 图

image-20230116213758538

描述内容与关系

分层结构

数据层 -> 逻辑层 -> 视图层

  • 数据层:数据 Model,外部数据的增删改查;
  • 逻辑层:业务 Enity,处理核心业务逻辑输出;
  • 视图层:视图 view,处理和外界的交互逻辑。

image-20230116214209934

组件工具

image-20230116214409843

总结

语言进阶部分用处挺大