Go语言进阶与依赖管理| 青训营笔记

92 阅读3分钟

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

青训营掘金Day02-Go语言进阶与依赖管理

1.语言进阶

  • 并发与并行区别
  • go语言就是并发而生的
  • 协程和线程区别 协程用户态由go语言去创建。 image.png

如何开启协程

快速打印Goroutine

通过go来开启协程

image.png

image.png

package main
​
import (
    "fmt"
    "time"
)
​
func hello(i int) {
    println("hello goroutine: " + fmt.Sprint(i))
}
​
func main() {
    for i := 0; i < 5; i++ {
        go func(j int) {
            hello(j)
        }(i)
    }
    time.Sleep(time.Second)
}
​

协程间怎么通信

  • 提倡通过通信共享内存而不是通过共享内存而实现通信
  • 通过通道channel 来通信

image.png

Channel

make(chan 元素类型,[ 缓存大小 ])

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

有缓冲通道其实看做是一个货架或者仓库,是有限的,当满了之后会阻塞发送image.png

package main
​
func main() {
    src := make(chan int)
    dest := make(chan int, 3)
    // A
    go func() {
        defer close(src)
​
        for i := 0; i < 10; i++ {
            src <- i
        }
    }()
    // B
    go func() {
        defer close(dest)
        for i := range src {
            dest <- i * i
        }
    }()
​
    // M
    for i := range dest {
        //复杂操作
        println(i)
    }
}

并发安全 Lock

go也保存对共享内存通信的机制

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 addWriteoutLock() {
    for i := 0; i < 2000; i++ {
        x += 1
    }
}
​
// 测试
func main() {
    x = 0
    // 没有加锁出现并发安全问题
    for i := 0; i < 5; i++ {
        go addWriteoutLock()
    }
​
    time.Sleep(time.Second)
    println("WithOutLock:", x)
    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
​
    time.Sleep(time.Second)
    println("WithLock:", x)
}
​

WaitGroup

前面都用sleep来阻塞(不优雅

image.png

计数器 开启协程+1;执行结束-1;主协程阻塞直到计数器为0。

package main
​
import (
    "fmt"
    "sync"
)
​
func hello1(i int) {
    println("hello goroutine: " + fmt.Sprint(i))
}
​
func main() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func(j int) {
            defer wg.Done()
            hello1(j)
        }(i)
    }
    wg.Wait()
    println("运行完成")
}
​

2.依赖管理

2.1背景

站在巨人的肩膀上

  1. 工程项目不可能基于标准库0~1编码搭建
  2. 管理依赖库

image.png 我们应该把更多精力关注在业务逻辑上,类似于java的Maven

2.2 依赖管理演进

image.png

  • 不同环境(项目)依赖的版本不同
  • 控制依赖库的版本

2.2.1 GOPATH

环境变量 $GOPATH,这个目录下面主要有三个文件夹bin、pkg、src

  • bin 项目编译的二进制文件
  • pkg 项目编译的中间产物、加速编译
  • src 项目源码
  1. 项目代码直接依赖src下的代码
  2. go get 下载最新版本的包到src目录下

弊端

image.png 场景: A 和 B依赖于某一个package的不同版本 问题无法实现package的多版本控制

2.2.2 Vendor

  • 项目目录下增加 vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor
  • 依赖寻址方式:vendor => GOPATH

image.png

通过每个项目引入一份依赖的副本解决了多个项目需要同一个package依赖的冲突问题

弊端

image.png

问题:

  • 无法控制依赖的版本
  • 更新项目又可能出现依赖冲突,导致编译出错。

2.2.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

image.png

依赖标识 :[Module Path][Version/Pseudo-version]

2.3.2 依赖配置-version

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

  • MAJOR: 不同的major间不兼容的,之间是代码隔离的
  • MINOR: 新增函数或功能,需要在major下兼容
  • PATCH: 修改bug

image.png

2.3.3 依赖配置-indirect

image.png

依赖关系 A->B->C: A->B 直接依赖,A->C 间接依赖

2.3.4 依赖配置-incompatible

image.png

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

2.3.5 依赖分发-回源|Proxy

image.png

  • 无法保证构建稳定性:增加/修改/删除软件版本
  • 无法保证依赖可用性:删除软件
  • 增加第三方压力:代码托管平台负载问题

通过一个Proxy代理来保证依赖的稳定性。

image.png

2.3.6 依赖分发-变量 GOPROXY

GOPROXY="proxy1.cn,https://proxy2.cn,…" 服务站点URL列表,"direct"表示源站

image.png

2.3.7 工具-go get

go-get是Golang的软件包管理工具。它允许用户下载、安装、更新和管理Golang项目中使用的第三方库。它与其他语言的包管理工具(如npm和pip)类似,可以自动处理依赖关系并确保所有库都是最新版本。

image.png

2.3.8 工具-go mod

Go Modules是Go语言的包管理工具,它是Go1.11版本中引入的一项功能。它支持管理Go程序的依赖关系,可以自动处理依赖关系并确保所有库都是最新版本。Go Modules可以让开发者管理本地和远程依赖关系,同时在项目中使用多种版本的库。它还支持在离线环境中工作,并具有版本控制和回滚功能。

image.png