Go语言-2.依赖管理&测试| 青训营

89 阅读4分钟

1 并发&并行

1.1概念

Golang 有针对性解决高并发问题。并行可以看作是一个实现高并发的手段。

  • 并行:多个程序在多个CPU上运行;
  • 并发:多线程程序在一个核的CPU上运行【时间片切换】。

Golang中的协程与线程的区别:

  • 协程:用户态;比线程更加轻量。
  • 线程:内核态;一个线程中可以跑多个协程。

在Go中,建立协程仅需在函数前添加go关键字即可。

1.2 Channel

通道:实现通过通信共享内存;通道支持先入先出保证顺序性。

channel的构建采用make函数,格式如下:make(chan 元素类型,[缓冲大小])

有缓冲的channel解决消费者消费能力不强的问题【此处和消息队列有些相似】

支持锁操作,对临界区加锁可以解决在并发过程中出现的安全性问题。【sync.Mutex】

WaitGroup【sync.WaitGroup】支持执行并发任务,提供add;Done;Wait分别实现添加任务个数、计时器减一和阻塞功能。

1.3 案例

1.3.1 channel函数案例测试

添加test测试文件,实现单元测试。

//demo1.go
package concurrence
func CalSquare() {
	src := make(chan int)
	dest := make(chan int, 5)
	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
		}
	}()
	for i := range dest {
		println(i)
	}
}
//demo1_test.go
package concurrence
import "testing"
func TestCalSquare(t *testing.T) {
	CalSquare()
}

通过test文件实现测试不使用go run指令,否则会报错,go run不执行带test的文件。使用go test指令测试。 go test指令会测试当前目录下全部带有test的文件。 image.png

1.3.2 WaitGroup

package concurrence
import (
	"fmt"
	"sync"
)
func hello(i int) {
	println("hello world : " + fmt.Sprint(i))
}
func ManyGo() {
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(j int) {
			defer wg.Done()
			hello(j)
		}(i)
	}
	wg.Wait()
}

其中,Add实现了添加1个任务。Done实现计时器逐步减一的操作。Wait实现任务完成后的阻塞活动。

2 依赖管理

这部分与Java的Maven;Python的pip|Conda类似,都是管理项目依赖工具。

2.1 发展

GOPATH->GOVENDOR->GOMODULE【最后一个是目前应用最广的】 各版本依赖工具具有的问题:

  • Go path:存在多版本依赖冲突问题;
  • Go vendor:添加vendor文件夹管理依赖,但是在多级依赖下,底层依赖依然会出现版本冲突问题。
  • Go module:通过go.mod文件管理依赖包。通过go getgo mod指令实现管理。

2.2 go.mod

在第一篇文章搭建go环境时,测试过程中对项目进行了初始化,然后产生go.mod文件,因此生成流程可参考之前内容。

go.mod文件中效果如下: image.png

  • 如上图所示,require()中的依赖项有 //indirect 标识。这个标识是因为存在间接依赖。【a依赖b,b依赖c,a间接依赖c】
  • 对于没有go.mod文件且主版本在2+的依赖,会添加+incompatible后缀。【可能有依赖冲突】
  • go会选择最低兼容版本进行使用。【如果a依赖b,a依赖c,b依赖dv1.0,c依赖dv1.1,在d的两个版本兼容的情况下,会选择dv1.1版本】

2.3 依赖分发

在导入依赖的过程中,依赖可能来源于Github、SVN等源,在没有任何代理的情况下,会存在以下问题:

  1. 不保证稳定性【代码版本可能被删】;
  2. 不保证依赖可用;
  3. 增强第三方负载。

因此引入Proxy解决该问题,保证依赖项的稳定可用。【proxy1;proxy2;...;源站依次查询】

go get 工具:

  • @update 默认
  • @none 删除依赖
  • @v版本号 tag版本、语义版本
  • @23dfdd5 特定commit
  • @master 分支最新commit

go mod 工具:

  • init:初始化
  • download:下载模块【拉依赖】
  • tidy:增加需要依赖,删除不需要的依赖

3 测试

3.1 单元测试

单元测试覆盖范围最广,基本是开发人员对函数、接口等内容的基础测试。

参考1.3中测试文件名,在单元测试中,文件名固定,都是在功能文件基础上补充“_test”结尾拼接成新文件。函数结构如下:

func TestXxx(* testing.T)

初始化逻辑放到TestMain中.【存在很多工具包支持equals等函数】

测试需要有较高的覆盖率【通常50~60;较高的为80%+】 代码构建过程中尽量小粒度,容易测试。

单元测试的目标:幂等【测试结果相同】、稳定【相互隔离】 比如,测数据库需要网络连接数据库,这样有网络对性能的影响,所以不稳定。

3.2 Mock测试

可以使用Mock函数【monkey.Patch;monkey.Unpatch】进行测试。为一个函数/方法打桩。

3.3 基准测试

主要针对性能方面。 函数以BenchmarkSelect开头

优化测试性能,可以使用fastrand,高并发场景可能会有一定的性能问题。

4 引入gin框架案例

  1. 初始化项目
go mod init page_ex
go get -u github.com/gin-gonic/gin

直接获取gin可能会报错,由于部分包在外网,难以访问,因此需要换源。【流程参考:www.cnblogs.com/xiaoyingzha…

成功换源后重新执行指令go get -u github.com/gin-gonic/gin【按照gin官网描述获取,参考gin-gonic.com/zh-cn/docs/…

  1. 构建测试demo1,代码如下:
package main
import (
	"github.com/gin-gonic/gin"
)
func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
	// GET:请求方式;/hello:请求的路径
	// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
	r.GET("/hello", func(c *gin.Context) {
		// c.JSON:返回JSON格式的数据
		c.JSON(200, gin.H{
			"message": "Hello world!",
		})
	})
	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
	r.Run()
}
  1. 打开终端输入go run指令启动服务。 image.png
  2. 使用浏览器访问localhost:8080/hello即可获取一个简单msg。 image.png

补充,成功导入gin框架后,go.mod中包含的内容如上述给出的go.mod示例图。