Day2 Go | 青训营笔记

90 阅读4分钟

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

1.1Goroutine

image.png

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

如图是示意图 image.png

1.3利用channel实现通道

同时也分为有缓冲通道以及无缓冲通道
make(chan 元素类型,[缓冲大小])

make(chan int)//无缓冲通道
make(chan int,2)//有缓冲通道

image.png

容量代表通道中能存放多少元素

带缓冲的通道能够尽可能避免生产者和消费者速度不均衡带来的效率问题:假如消费者的效率较低,就会阻塞生产者的生产效率,如果通道有缓冲,那么生产者就会把数据提前放到通道中去,然后执行其他指令

1.4 Go也保存通过共享内存来实现通信

var (
    x int64
    lock sync.Mutex
)
func addWithLock() {
    for i := 0; i < 200; i++ {
        lock.Lock()
        x += 1
        lock.Unlo                     ck()
    }
}
func addWithoutLack() {//未使用锁,会导致undefind报错,数据错误
    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( args...:"WithoutLack:", x)
    x = 0
    for i := 0; i < 5; i++{
      goaddWithLock()
    }
    time.Sleep(time.Second)
    println("args...WithLock:", x)

image.png

1.5 WaitGroup

实际程序运行的过程中,主协程并不知道子协程的结束时间,盲目的使用sleep()是不规范的,所以使用WaitGroup计数器。开始定义数字(Add(delta int)),每结束一个协程,协程就减一(Done()),主线程阻塞到Wait(),直到计数器为0,主协程就会继续执行下去

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

2依赖管理

go依赖包的演变进程

2.1GOPATH是go语言支持的一个环境变量

目录结构有:

  1. src 存放go项目的源码
  2. pkg 存放编译的中间产物,加快编译速度
  3. bin 存放go项目编译生成的二进制文件

出现的弊端:
gopath中,项目代码直接依赖src下面的代码
假设有两个项目,第一个项目A依赖v1版本 package的函数a()。第二个项目B,依赖v2版本package的函数b()。但是V2版本并没有兼容v1版本,因为两个项目同时依赖于src下的package包,一旦版本更新,项目不能够同时构建成功,也就无法实现多版本控制
如图: image.png

2.2go vender

此做法是在项目目录下增加一个wender文件夹,通过给每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。
在vendor机制下, 如果当前项目存在vendor目录,优先使用该目录下的依赖,如果依赖不存在,会从go path中寻找 但是会出现另一种问题。
如图所示: image.png 弊端通过vendor的管理模式,不能很好的控制对 D 的版本依赖。一旦更新项目,就可能会带来冲突,Vendor不能很清晰的标识依赖版本概念。

2.1.3

Go module GO官方推出的依赖管理系统

2.2依赖管理有三要素

  1. 配置文件描述依赖 go.mod
  2. 中心仓库管理依赖库proxy
  3. 本地工具go get/mod

可以使用第三方平台进行依赖托管,但是第三方平台的服务压力增加,使用go proxy,解决这些问题,他是一个服务站点,会缓存站中的软件内容,缓存的软件版本不会改变,而且原站软件删除之后仍然可用,从而实现了依赖分发。

测试是避免事故的最后一道屏障
测试一般分为:

  1. 单元测试
  2. 集成测试
  3. 回归测试

单元测试覆盖率是最大的,成本是最低的,回归测试是覆盖率最低的,成本最大的 image.png

3.test测试

如图所示 image.png

所有的测试文件都以_test.go结尾
测试函数的格式如下

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

初始化逻辑需要放到TestMain中 也可以使用第三方工具assert——github

弊端
假如已经编写好,Test测试文件,对一个文件内容进行测试(例如:替换字符)如果文本,字符被修改(删除) 那么,text测试文件就不能使用了

测试覆盖率添加命令,如图所示

func JudgePassLine(scare int16) bool {
    if score > 60 {
        return true
    }
    return false
}
func TestJudgePassLineTrue(t *testing.T){
    isPass := JudgePassLine(score: 70)
    assert.Equal(t,expected:true,isPass)
 }

image.png

  1. 一般覆盖率: 50%~60%,较高覆盖率80%+
  2. 测试分支相互独立、全面覆盖
  3. 测试单元粒度足够小,函数单一职责

3.4单元测试,Mock

工具,Github地址
可以在任何时间,任何环境去执行,完全不依赖本地文件,保证测试的稳定性。
打桩测试

3.5基准测试

测试一段程序运行时的性能,CPU的损耗,实际开发过程中,可能会遇到代码性能瓶颈,如果想要定位问题,就会对代码进行性能分析,就会用到基准测试

在完成项目之后,一定要做好完备的单元测试

4.

分层结构

  1. 数据层,数据model,主要负责外部数据的增删改查
  2. 逻辑层,业务Entity,处理核心业务逻辑输出
  3. 视图层,视图view,处理和外部的交互逻辑 分层结构设计github

分层结构不是固定的,适合的才是最好的
Gin,github

(项目分析待续) 地址