go 语言进阶与依赖管理 | 青训营

48 阅读4分钟

并发 vs 并行

  • 并发
    • 多线程程序在单核CPU上的执行 ,通过CPU时间片的快速切换去达到程序同时执行的效果。
  • 并行
    • 多线程程序在多核CPU上的执行,不行的程序运行在不同的核上,是正真意义上的并行(同时执行),并行是实现并发的一种手段。

go协程goroutine

较线程相比,协程是用户态的一种资源,创建代价比较小,协程栈在KB级别,协程的创建由go语言本身去实现;相反线程是内核态的一种资源,其创建和销毁的代价比较大,线程栈在MB级别。

协程通信

在golang中提倡通过通信来共享内存,而不是通过共享内存来实现通信。如果要用共享内存实现通信的话就需要对其进行互斥操作。

Channel/通道

  • 创建通道的方式

// type表示元素类型,buffer_size表示缓冲区大小 make(chan type,[buffer_size]) //创建无缓冲通道 make(chan int) //创建有缓冲通道 make(chan int, 4)

  • 无缓冲通道 vs 有缓冲通道

    • 无缓冲的通道又称为阻塞的通道,无缓冲的通道只有在有人接收值的时候才能发送值。就像你住的小区没有快递柜和代收点,快递员给你打电话必须要把这个物品送到你的手中,简单来说就是无缓冲的通道必须有接收才能发送。
    • 使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道。
    • 有缓冲通道就像你小区的快递柜只有那么个多格子,格子满了就装不下了,就阻塞了,等到别人取走一个快递员就能往里面放一个。

WaitGroup

在代码中生硬的使用time.Sleep肯定是不合适的,因为我们不能精确的知道程序执行的一个时间。go语言中可以使用sync.WaitGroup来实现并发任务的同步。

sync.WaitGroup有以下几个方法:

方法名功能
(wg * WaitGroup) Add(delta int)计数器+delta
(wg *WaitGroup) Done()计数器-1
(wg *WaitGroup) Wait()阻塞直到计数器变为0

sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。

依赖管理

  1. 不同环境/项目依赖的版本不同
  2. 控制依赖库的版本

GOPATH

  • bin:项目编译的二进制文件
  • Pkg:项目编译的中间产物,可加速编译

GOPATH 弊端

如上图所示,A 和 B 依赖某一 pkg 的不同版本,存在的问题:无法实现 pkg 多版本控制

GO Vendor

在项目目录下增加vendor文件夹,所有依赖包副本形式放在$project/vendor下,优先从该文件夹下去获取依赖

Go Vendor 弊端

项目 A 同时依赖 package B 和 package C,package B 和 package C 同时依赖 package D(D 有 V1和 V2两个版本),利用 vendor 的方式就不能控制 V1和 V2的选择(本质上是因为 vendor 还是依赖的项目的源码),于是就是出现下面问题:

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

Go Module

定义版本规则和管理项目依赖关系

  • 通过go.mod文件管理依赖包版本
  • 通过go get/go mod指令工具管理依赖包

依赖管理三要素

  • 配置文件,描述依赖 go.mod
  • 中心仓库管理依赖库 Proxy
  • 本地工具 go get/mod

go.mod

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

依赖配置-version

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

MAJOR:大版本,不同的MAJOR是可以不兼容的 MINOR:新增一些功能函数,在大版本基础上是兼容的 PATCH:代码bug修复

v1.3.0

v2.3.0

  • 基于commit伪版本

版本前缀(和语义化版本一致)-时间戳(提交本次commit的时间戳)-提交commit12位的hash码

v0.0.0-20180306012644-bacd9c7ef1dd

依赖配置-indirect

存在: A->B->C

A 直接依赖 B,A 间接依赖 C,在 go.mod 中对于没有直接依赖的标识为 indirect

依赖分发- Proxy

直接使用第三方平台的话会存在以下问题:

  • 无法保证构建稳定性,比如存在增加、修改、删除软件版本的话,后续构建可能会存在依赖找不到了,导致构建失败
  • 无法保证依赖的可用性,比如依赖仓库被删除了
  • 增加第三方压力

为了解决以上问题,就有了 GOPROXY。

GOPROXY 是一个服务站点,会缓存源站中的软件内容,缓存的软件版本不会改变,并且源站软件删除之后依然可用,从而实现稳定和可靠的依赖分发。

// 项目依赖查找顺序proxy1->proxy2->direct
// 如果在前面都没有找见,则直接到源站查找
GOPROXY="https://proxy1.cn,https://proxy2.cn,direct"

工具 go mod

go mod init //初始化,创建go.mod文件 
go mod download //下载模块到本地缓存 
go mod tidy // 增加需要的依赖,删除不需要的依赖

工具 go get

@update //获取默认的依赖包 
@none //删除依赖 
@v1.2.2 //获取对应语义版本的依赖包 
@23dfff3 //获取对应commit的依赖 
@master //获取最新分支的依赖包 
//比如 go get code.byted.org/tmq/tmq@v1.5.30