Go语言入门——工程实践 | 青训营

84 阅读4分钟

Go语言入门——工程实践 7.26

1.语言进阶

1.1 Go routine

快速打印hello goroutine : 0~ hello goroutine : 4

image-20230816202727017.png

image-20230816202744887.png

1.2 CSP

CSP(Communicating Sequential Processes)

image-20230816202822682.png

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

1.3 Channel

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

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

image-20230816202941377.png

image-20230816203037868.png

A子协程发送0~9数字

B子协程计算输入数字的平方

主协程输出最后的平方数

1.4 并发安全lock

对变量执行2000次+1操作,5个协程并发执行

image-20230816203150248.png

image-20230816203207220.png

1.5 WaitGroup

image-20230816203358591.png

计数器

开启协程+1;执行结束-1;主协程阻塞直到计数器为0。

快速打印 hello goroutine : 0~hello goroutine : 4

image-20230816203528020.png

image-20230816203538672.png

2.依赖管理

2.1 依赖管理演进

image-20230816203719221.png

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

2.1.1 GOPATH

image-20230816203815182.png

环境变量 $GOPATH

项目代码直接依赖 src 下的代码

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

弊端

image-20230816204021078.png

场景:A和B依赖于某一 package的不同版本

问题:无法实现package的多版本控制

2.1.2 Go Vender

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

image-20230816204404678.png

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

弊端

image-20230816204456296.png

问题

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

2.1.3 Go Module

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

终极目标:定义版本规则和管理项目依赖关系

2.2 依赖管理三要素

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

2.3 依赖管理

2.3.1 依赖配置-go.mod

image-20230816204730851.png

依赖标识:[Module Path] [Version / Pseudo-version]

2.3.2 依赖配置-version

  • 语义化版本

${MAJOR}.${MINOR}.${PATCH}

V1.3.0

V2.3.0

  • 基于commit伪版本

vx.0.0-yyymmddhhmmss -abcdefgh1234

v0.0.0- 20220401081311-c38fb59326b7

v1.0.0- 20201130134442-10cb98267c6c

2.3.3 依赖配置-indirect

image-20230816205009101.png

A->B->C

A->B 直接依赖

A->C 间接依赖

2.3.4 依赖配置-incompatible

image-20230816205103482.png

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

依赖图

image-20230816205215937.png

如果X项目依赖了A、B两个项目,且A、B分别依赖了C项目的v1.3、v1.4两个版本,最终编译时所使用的C项目的版本为如下哪个选项?(单选)

A. v1.3

B. v1.4

C. A用到C时用v1. 3编译, B用到C时用v1.4编译

选择 B,选择最低的兼容版本

2.3.5 依赖分发

回源

image-20230816205410873.png

  • 无法保证构建稳定性 增加/修改/删除软件版本
  • 无法保证依赖可用性 删除软件
  • 增加第三方压力 代码托管平台负载问题

Proxy

image-20230816205517404.png

2.3.6 依赖分发-变量GOPROXY

GOPROXY = “proxy1.cn, proxy2.cn ,direct” 服务站点URL列表,“direct” 表示源站

Proxy1—> Proxy2—> Direct

2.3.7 工具-go get

go get example.org/ pkg

@update 默认

@none 删除依赖

@v1.1.2 tag版本,语义版本

@23dfdd5 特定的commit

@master 分支的最新commit

2.3.8 工具-go mod

go mod

init 初始化,创建go.mod文件

download 下载模块到本地缓存

tidy 增加需要的依赖,删除不需要的依赖

3.测试

image-20230816210008947.png

测试是避免事故的最后一道屏障

image-20230816210047250.png

从上到下,覆盖率逐层变大,成本却逐层降低

3.1 单元测试

image-20230816210120833.png

3.1.1 单元测试-规则

  • 所有测试文件以test.go 结尾

image-20230816210200760.png

  • func TestXxx(*testing.T)

image-20230816210209193.png

  • 初始化逻辑放到TestMain中

image-20230816210219987.png

3.1.2 单元测试-例子

image-20230816210234734.png

image-20230816210244238.png

3.1.3 单元测试-运行

go test [flags][packages]

image-20230816210300253.png

3.1.4 单元测试-assert

image-20230816210430548.png

image-20230816210558206.png

image-20230816210609939.png

3.1.5 单元测试-覆盖率

如何衡量代码是否经过了足够的测试?

如何评价项目的测试水准?

如何评估项目是否达到了高水准测试等级?

代码覆盖率

image-20230816210649762.png

测试1

image-20230816210704634.png

image-20230816210714586.png

测试2

image-20230816210744097.png

image-20230816210803003.png

Tips:

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

3.2 单元测试-依赖

image-20230816210901115.png

外部依赖 => 稳定&幂等

3.3 单元测试-文件处理

image-20230816211022418.png

image-20230816211047887.png

3.4 单元测试-Mock

image-20230816211127575.png

快速Mock函数

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

image-20230816211158667.png

对ReadFirstLine打桩测试,不再依赖本地文件

3.5 基准测试

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

3.5.1 基准测试-例子

image-20230816211241270.png

随机选择执行服务器

3.5.2 基准测试-运行

image-20230816211304547.png

image-20230816211316670.png

3.5.3 基准测试-优化

image-20230816211329423.png

image-20230816211339393.png

4.项目实践

4.1 需求描述

社区话题页面

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

4.2 需求用例

浏览消费用户

image-20230816211436121.png

4.3 ER图

Entity Relationship Diagram

话题 帖子

image-20230816211506748.png

4.4 分层结构

image-20230816211611728.png

数据层:数据Model,外部数据的增删改查

逻辑层:业务Entity,处理核心业务逻辑输出

视图层:视图view,处理和外部的交互逻辑

4.5 组件工具

  • Gin高性能go web框架

https:// github.com/ gin-gonic/ gin#installation

  • Go Mod

go mod init

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

4.6 Repository

Topic

image-20230816211921704.png

QueryTopicByld

Post

image-20230816211946405.png

QueryPostsByParentId

如何实现查询

Index

image-20230816212022392.png

image-20230816212029739.png

初始化题数据索引

image-20230816212053588.png

查询

image-20230816212129053.png

索引:话题ID

数据:话题

索引:话题ID

数据:帖子列表

4.7 Service

实体

image-20230816212403517.png

流程

image-20230816212412761.png

代码流程编排

image-20230816212431228.png

可用性

image-20230816212451569.png

并行处理

image-20230816212504311.png

4.8 Controller

构建View对象

业务错误码

image-20230816212521465.png

4.9 Router

image-20230816212550946.png

初始化数据索引

初始化引擎配置

构建路由

启动服务

4.10 运行

运行测试

go run server.go