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

135 阅读4分钟

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

本篇笔记对第二节课《Go语言上手 - 工程实践》的知识点进行汇总。本节课的内容主要分为并发编程、依赖管理、单元测试、项目实战四个部分。

并发编程

并发与并行

  • 并发:指在同一时刻只执行一条指令执行。多个进程的情况下,把时间分为若干端,指令被快速的轮换执行,宏观上具有多个进程同时执行的效果。
  • 并行:指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。

image.png

进程、线程与协程

  • 进程:进程是程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的独立单位,是应用程序运行的载体。
  • 线程:线程是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,各个线程之间共享进程的内存空间。
  • 协程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制 栈为KB级别。

CSP(Communicating Sequential Processes)

image.png

协程在Go语言中是通过通信共享内存,使用goroutine和channel实现的CSP并发模型。 channel分为无缓冲和有缓冲。创建channel的代码为:

make(chan int {, 2}) // 花括号内表示有缓冲通道的创建

并发安全Lock

多个goroutine同时访问某一块内存,可能会存在并发安全的问题。

使用等待组WaitGroup进行多个任务的同步,保证在并发环境下的指定任务数量的完成。WaitGroup的常用几个函数如下:

方法名功能
(wg * WaitGroup) Add(delta int)等待组的计数器 +1
(wg * WaitGroup) Done()等待组的计数器 -1
(wg * WaitGroup) Wait()当等待组计数器不等于 0 时阻塞直到变 0。

依赖管理

依赖管理的演化过程: GoPath → Go Vendor → Go Module

GoPath

  • 环境变量 $GOPATH
bin  //项目编译的二进制文件
pkg  // 项目编译的中间产物,加速编译
src  //项目源码
  • 项目代码直接依赖src下的代码
  • go get 下载最新版本的包到src目录下

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

Go Vendor

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

通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。 弊端:无法控制依赖的版本;更新项目又可能出现依赖冲突,导致编译出错。

Go Module

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

依赖管理的三要素:

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

依赖图:

image.png

测试

单元测试

单元测试规则:

  • 测试文件均以“_test.go”结尾;
  • 测试函数命名“func TestXxx(t *testing.T)”
  • 初始化逻辑放在TestMain中
func TestMain(m *testing.M){
    //测试前:数据装载、配置初始化等前置工作
    
    code := m.Run()
    
    // 测试后:释放资源等收尾工作
    
    os.Exit(code)
}

Tips:

  • 一般覆盖率:50~60%,较高覆盖率80%+
  • 测试分支相对独立、全面覆盖
  • 测试单元粒度足够小,函数单一职责

基准测试

  • 优化代码,需要对当前代码分析
  • 内置的测试框架提供了基准测试的能力

项目实战

项目说明:社区话题页面

需求描述:

  • 展示话题(标题,文字描述)和回帖列表
  • 暂不考虑前端页面实现,仅仅实现一个本地web服务
  • 话题和回帖数据用文件存储

需求用例: 用户与哪些对象进行交互

ER 图: 描述对象的属性及相互关系

分层结构:

  • 数据层:数据 Model,外部数据的增删改查
  • 逻辑层:业务 Entity,处理核心业务逻辑
  • 视图层:视图 View,处理和外部的交互逻辑

组件工具:

  • gin:高性能开源的 go web 框架,基于此搭建 web 服务器,主要使用到路由分发。
  • Repository: 首先,定义业务对象结构体(Topic,Post),实现查询对象函数(QueryTopicById、QueryPostsByParentId)
  • Service: 首先,定义 service 实体 其次流程,参数校验→准备数据→组装实体
  • Controller: 构建 View 对象 业务错误码
  • Router: 初始化数据索引,初始化引擎配置,构建路由,启动服务