这是我参与的「第三届青训营 -后端场」笔记创作活动的的第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)
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
语义化版本
{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