Go语言进阶-工程进阶 | 青训营笔记

109 阅读2分钟

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

课程学习:

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

本堂课知识要点如下

1、语言进阶

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

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

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

1.1 Goroutine

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

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

1.2 CSP

共享内存实现通信:互斥对内存进行加锁

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

1.3 Channel

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

无缓冲通道 make(chan int)

有缓存通道 make(chan int,2)

示例:

A 子协程发送

B 子线程计算输入数字的平方,主协程输出最后的平方数

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 {dest <- i*i}
}()

1.4 并发安全Lock

示例:

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

每执行一次+1操作,都对临界区进行一次加锁和解锁

func addWithLock() {
        for i := 0;i < 2000;i++ {
                lock.Lock()
                x += 1
                lock.Unlock()
        }
}

1.5 WaitGroup

Add(delta int) Done() Wait()

实际上是一个计数器,开启协程+1;执行结束-1;主协程阻塞直到计数器为0,等待所有并发任务结束

示例:快速打印

func ManyGoWait() {
        var wg sync.WaitGroup
        wq.Add(5) // 开启5个协程
        for i := 0;i < 5:i++ {
                go func(j int) {
                        defer wg.Done()
                        hello(j)
                }(i)
        }
        wg.Wait()
}

2、依赖管理

各种开发包

2.2依赖管理三要素

1.配置文件,描述依赖 go.mod

2.中心仓库管理依赖库 Proxy

3.本地工具 go get/mod

2.3.1 依赖配置-go.mod

module 依赖管理基本单元 go 1.16 原生库 require() 单元依赖

2.3.2 version

2.3.3 indirect

没有直接依赖,如间接依赖,用indirect标识

2.3.4 incompatible

主版本2+模块会在模块路径增加/vN后缀

对于没有 go.mod 文件并且主版本2+的依赖,会+incompatible

依赖图

项目的版本选择最低的兼容版本

2.3.5 依赖分发

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

Proxy:服务站点,缓存原站中的文件内容

2.3.6 变量GOPROXY

2.3.7 工具-go get

2.3.8 go mod

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

download:下载模块到本地缓存

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

3、测试

单元测试、Mock测试、基准测试

3.1.1 单元测试-规则

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

3.1.2 例子

func HelloTom() string {return "Jerry"}
func TestHelloTom(t *testing.T) {
        output := HelloTom()
        expectOutput := "Tom"
        if output != expectOutput {t.Error()}
}

3.1.3 运行

3.1.4 assert

func TestHelloTom(t *testing.T) {
        output := HelloTom()
        expectOutput := "Tom"
        assert.Equal(t,expectOutput,output)
}

3.1.5 代码覆盖率

衡量代码是否经过了足够的测试 评价项目的测试水准 评估项目是否达到了高水准测试等级

代码执行的覆盖率

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

3.2 依赖

3.3 文件处理

函数执行依赖文件,但文件有被修改的可能

3.4 Mock

快速 Mock 函数 为一个函数打桩;为一个方法打桩

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

func TestProcessFirstLineWithMock(t *testing.T) {
        monkey.Patch(ReadFirstLine,func() string {
        return "line110"
        })
        defer monkey.Unpatch(ReadFirstLine)
        line := ProcessFirstLine()
        assert.Equal(t,"line000",line)
}

3.5 基准测试

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

4、项目实战

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

示例: 展示话题(标题、文字描述)和回帖列表;实现一个本地web服务;话题和回帖数据用文件存储

4.2 需求用用例

浏览消费用户,话题、贴子

4.3 ER图

4.4 分层结构

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

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

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

4.5 组件工具

框架依赖

Gin高性能 go web 框架

Go Mod

4.6 Repository

实现查询: 初始化话题数据索引

4.7 Service

定义一个实体:

type PageInfo struct {
        Topic *repository.Topic
        PostList []*repository.Post
}

流程:参数校验->准备数据->组装实体

话题信息和回帖信息并行处理

4.8 Controller

构建View对象、业务错误码

4.9 Router

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

4.10 运行

课后个人总结:

今天讲课的知识点比较多,因为老师是分点讲的,所有记起笔记来并不难,然后就是大部分的知识点还是比较好理解的,个别难点初次接触难以理解,如依赖管理中关于version、indirect、incompatioble等知识点,测试中的Mock测试以及基准测试等,今天的重点难点就是项目实战了,虽然通过听课大致了解了项目设计到完成的基本流程,但关于代码部分的内容还是不太懂,课后我会去阅读项目代码,加以理解。