GOlangNO.2 :工程实践 | 青训营笔记

140 阅读2分钟

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

GOLANG

1.高并发

1.1 Goroutine

线程:内核态,线程跑多个协程,栈MB级别。 携程:用户态,轻量级线程,栈KB级别。

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


package main

import (
	"fmt"
	"time"
)


func hello(i int) {
	println("Hello goroutine :"+fmt.Sprint(i))
}

func HelloGoRoutine(){
	for i := 0; i < 5; i++ {
		go func(j int){
			hello(j)
		}(i)
	}

	time.Sleep(time.Second)
}

func main(){
	HelloGoRoutine();
}

1.2 CSP(Communicating Sequential Processes)

通信共享内存.png

1.3 Channel

定义语法: make(chan 元素类型,[缓冲大小]
有缓冲通道: make(chan int,2)
无缓冲通道: make(chan int)

案例:生产者和消费者模式


package main

import (
)


func CalSquare (){
	src := make(chan int)
	dest := make(chan int ,3) 
	//带缓冲chan可以解决生产者和消费者效率差异问题
	//生产者
	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)
	}
}

func main(){
	CalSquare()
}

1.4 并发锁

案例:测试并发编程加锁和不加锁的数据准确性

package main

import (
	"sync"
	"time"
)

var  (
	x int64
	lock sync.Mutex
)

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

//不加锁做增加
func addWithoutLock(){
	for i := 0; i < 2000; i++ {
		x += 1
	}
}

func Add(){
	x = 0
	for i := 0; i < 5; i++ {
		go addWithoutLock()
	}
	time.Sleep(time.Second)
	println("WithoutLock :",x)
	x = 0
	for i := 0; i < 5; i++ {
		go addWithLock()
	}
	time.Sleep(time.Second)
	println("WithLock :",x)
}

func main(){
	Add()
}

1.5 WaitGroup

使用计数器测试并发时的并发量 案例:改进1.1的代码

package main

import (
	"fmt"
	"sync"
)

func hello(i int) {
	println("Hello goroutine :"+fmt.Sprint(i))
}

func ManyGoWait (){
	var wg sync.WaitGroup
	wg.Add(5)	//计数器+5
	for i := 0; i < 5; i++ {
		go func (j int)  {
			defer wg.Done() 	//计数器-1
			hello(j)
		}(i)
	}
	wg.Wait()	//阻塞直到计数器为0
}

func main(){
	ManyGoWait()
}


2.依赖管理

2.1 Go依赖管理演进

GOPATH--->GO Vendor--->GO Module 不同环境依赖的版本不同。控制依赖库的版本。

2.1.1 GOPATH

内涵三个目录: bin,pkg,src
bin:项目编译的二进制文件
pkg:项目编译的中间产物,加速编译
src:项目源码 (go get将依赖包拷到src下)
GOPATH无法实现版本的控制。

2.1.2 GO Vendor

项目目录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor下
依赖寻址方式: vendor --> GOPATH
其解决了多个项目需要一个package依赖冲突的问题。
但是无法控制依赖的版本,更新项目可能出现依赖冲突。

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

module example/project/app //依赖管理基本单元
go 1.18   //原生库
require (      //单元依赖
    example/lib1 v1.0.2
)

2.3.2 依赖配置-version

语义化版本
MAJOR.{MAJOR}.{MINOR}.${PATCH}
V1.2.0


基于commit的伪版本
vX.0.0-yyyymmddhhmmss-abcdefgh1234

2.3.3 依赖配置-indirect

非直接依赖。

2.3.4 依赖配置-incompatible

对于没用go.mod文件并且主版本2+的依赖,会加该关键字
如果不同包分别依赖于相同的不同版本的依赖。其会选择最低的兼容版本。

2.3.5 工具-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. 测试

3.1 测试的分类

1.回归测试
2.集成测试
3.单元测试
从上到下,覆盖率逐渐变大,成本逐渐降低。

3.2 单元测试

3.2.1 规则

1.所有测试文件以_test.go结尾。 2.func TestXxx(*testing.T)

func TestMain (m *testing.M){
    //测试前:数据转载,配置初始化等
    code := m.Run()
    //测试后:释放资源等收尾工作
    
    os.Exit(code)
}

3.2.2 运行

语法:go test [test文件] [原文件]

3.2.2 assert

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

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

3.2.3 覆盖率

语法:go test judgment_test.go judgment.go --cover
覆盖率的算法: 已执行的代码行数 / 总代码行数

3.2.4 Mock打桩

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

4.项目实战

4.1 需求描述

社区话题页面
1.展示话题(标题,文字描述)和回帖列表
2.话题和回帖用文件存储

4.2 引入组件依赖

指令:go mod init
go get gopkg.in/gin-gonic/gin.v1@v1.3.0

4.3 持久层Repository的实现