Go 语言入门 - 工程实践 | 青训营笔记

144 阅读5分钟

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

语言进阶

并发与并行的区别

  • 并行是指两个或者多个事件在同一时刻发生,而并发是指两个或多个事件在同一时间间隔发生。
  • 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
  • 个人理解:并发是对于单个cpu核而言的,在计算机运行的时候,有许多指令会使cpu在空闲状态,等待指令完成,这时候cpu可以先处理下一个任务,并发在同一时刻只有一个任务在执行。而并行是对于多个cpu核的,指的是多个任务同时在不同的cpu核上运行。

线程和协程的区别

  • 协程是一种轻量级的线程,因为线程的创建必须进行操作系统的调用,而协程的创建则是由编程语言完成的,消耗的资源小得多
  • 两者要解决的问题不同,线程可以并发也可以并行,而协程只能并发,是用于解决并发问题的。

并发安全问题

  • 加上Lock锁

WaitGroup

	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func(j int) {
			defer wg.Done()
			hello(j)
		}(i)
	}
	wg.Wait()
}

通过计时器来知晓所有并发任务实现完成,Add表示有几个协程,Done表示一个协程完成后-1,wait用于阻塞知道计数器为0

依赖管理

go依赖管理演进

GOPATH->Go Vender->Go Module

Go Module

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

依赖管理三要素

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

微信图片_20220508133112.png

微信图片_20220508133209.png

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

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

由于直接使用第三方代码托管平台会无法保证构建稳定性,无法保证依赖可用性,并且会增加第三方压力,所以出现了Proxy

3. 本地工具 go get/mod

微信图片_20220508134850.png

微信图片_20220508134855.png

测试

测试包括:回归测试,集成测试,单元测试,从左至右覆盖率逐渐变大,成本缺逐渐降低。

单元测试

微信图片_20220510132818.png

单元测试的规则

微信图片_20220510133036.png

单元测试的例子

package main

func HelloTom() string {
	return "Jerry"
}  //源程序
package main

import "testing"

func TestHelloTom(t *testing.T) {
	output := HelloTom()
	expectOutput := "Tom"
	if output != expectOutput {
		t.Errorf("Expected %s do not match actual %s", expectOutput, output)
	}
}  //测试程序

然后只需要运行测试程序,或者在终端在本文件目录下执行go test命令即可

开源assert包的使用

package main

import (
	"testing"
	"github.com/stretchr/testify/assert"
)

func TestHelloTom(t *testing.T) {
	output := HelloTom()
	expectOutput := "Tom"
	assert.Equal(t,expectOutput,output)
}  //用开源的assert包来进行equal和not equal的判断

这里使用了github上的开源包,对于包的获取只需要在终端执行 go get github.com/stretchr/testify/assert即可

单元测试的评估-覆盖率

package main

func JudgePassLine(score int64) bool {
	if score >= 60 {
		return true
	}
	return false
}  //源程序
package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestJudgePassLineTrue(t *testing.T) {
	isPass := JudgePassLine(70)
	assert.Equal(t, true, isPass)
}  //测试程序

在终端执行go test -cover即可获取覆盖率

微信图片_20220510145843.png

单元测试稳定性-mock包

项目通常会有许多依赖,如数据库和本地文件,但是单元测试需要每次测试的结果一样,并且在任何时间任何函数都能独立运行,即幂等和稳定。为了达到稳定性这个目标,我们引入了mock机制

package test

import (
	"bufio"
	"os"
	"strings"
)

func ReadFirstLine() string {
	open, err := os.Open("log")
	defer open.Close()
	if err != nil {
		return ""
	}
	scanner := bufio.NewScanner(open)
	for scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

func ProcessFirstLine() string {
	line := ReadFirstLine()
	destLine := strings.ReplaceAll(line, "11", "00")
	return destLine
}  //源程序

这里的源程序就需要依赖本地文件log,当log文件发生变动,就会影响测试的结果,所以我们引入了monkey这个包来进行mock操作,为函数打桩,使测试结果不再依赖本地文件

package test

import (
	"bou.ke/monkey"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestProcessFirstLine(t *testing.T) {
	firstLine := ProcessFirstLine()
	assert.Equal(t, "line00", firstLine)
}

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

基准测试

在实际项目过程中,我们有时候会遇到性能瓶颈的问题,这时候我们就需要改进项目的性能,为了定位问题,我们就需要对代码进行性能分析,这就是基准测试的意义。

基准测试的使用和单元测试非常相似,只需要将函数命名前缀从单元测试的前缀Test改为Bench,传入的参数从testing.A改为testing.B,然后在执行的命令加上后缀-bench即可,这里放上一个基准测试的函数和运行结果方便比较。

func BenchmarkSelect(b *testing.B) {
	InitServerIndex()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Select()
	}
}

微信图片_20220510162516.png tips:使用vscode在编写好测试函数后,函数上方会有运行的按钮,比较方便,即下图左上角

微信图片_20220510162847.png

项目实践-社区话题页面

需求描述

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

模型ER图

微信图片_20220510173125.png

项目通用分层结构

结构需要根据项目的不同进行调整,不必要硬套 微信图片_20220510173211.png

组件工具

项目需要用到gin框架

go mod init {项目名} //初始化mod文件
go get gopkg.in/gin-gonic/gin.v1@v1.3.0 //下载gin框架

在gin框架的下载过程中遇到了很多问题,这篇文章成功的解决了这些问题(blog.csdn.net/qq_34284638…)

运行结果

具体函数的编写和逻辑较为复杂,这里不赘述了(根本不会),这里只讲一下怎么测试服务。

curl 127.0.0.1:8080/community/page/get/2  //终端输入curl命令

也可以在浏览器直接访问localhost:8080/community/page/get/1 运行结果如图

微信图片_20220510174625.png