Go 依赖管理学习 | 青训营笔记

97 阅读3分钟

Go 依赖管理


GO 依赖管理目标

  1. 实现不同环境(项目)下依赖的版本不同
  2. 可以做到控制依赖库的目标

Go Path

GO 中主要含有以下三个文件:

  • bin : 用于存储项目变异的二进制文件
  • pkg:项目编译的中间产物,可以加速编译
  • src:用于存储项目源码

项目直接依赖 src 下的代码,使用 go get 下载最新版本的包到 src 目录下。

Go Path 的缺点

当有两个项目同时依赖于一个 package 的不同版本时,不同版本的包会产生冲突导致这两个项目无法同时运行,即,无法实现 package 的版本控制。

Go Vendor

为了解决 Go Path 的多版本控制问题,Go Vendor 在每个项目目录下增加 vendor 文件,用于存放该项目依赖的 package 副本,Go Path 作为依赖的主仓库,Project/vendor 作为副本仓库。解决了多个项目需要同一个 package 依赖的冲突问题。如果在 vendor 没有找到的依赖,就会去 $GOPATH 找。

Go Vendor 缺点:

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

以上两种方法,都是直接依赖项目的源码,而不能很好的标识区分版本关系。

Go Module

Go 语言官方推出的依赖管理工具,对比以上两种方式,有以下改进:

  • 通过 go.mod 文件管理依赖包版本
  • 通过 go get/go mod 指令工具管理依赖包
  • go mod 可以定义版本规则和管理项目依赖关系

依赖管理三要素

  1. 配置文件,描述依赖。可以唯一定位依赖的位置,对应 go.mod
  2. 有一个中心仓库管理依赖仓库,对应 Proxy 文件
  3. 要有本地工具
module example/...  # 依赖管理基本单元

go 1.16   # 原生库

require (
    example/lib1 v1.0.2
    ...
)

go.mod 中包含以下三个部分:

  • 模块路径: 标识可以从哪里找到这个模块,
  • 原生库:标识go 的版本
  • 单元依赖:[Module Path][Version/Pseudo-version],其中 Version就可以唯一定位依赖的某一个版本号,某一次提交。

Go Module 的版本规则

go 为了表示单元依赖的版本推出了两种标识版本的方式:语义化版本和基于commit 的伪版本方式

语义化版本

形式:${MAJOR}.${MINOR}.${PATCH}

  • MAJOR 定义一个大版本的编号,不同的 MAJOR 之间可以代码不兼容

  • MINOR 定义新增函数,他们需要在同一个 MAJOR 之间保持兼容

  • PATCH 表示一些bug的修复

基于commit的伪版本

形式:vx.0.0-yyyymmddhhmmss-abcdefgh12xx

  • 版本前缀-时间戳-12位哈希码

  • 每次提交代码,Go 会自动生成一个伪版本号

关键字

go.mod 中还使用关键字表示依赖的引用关系

  • indirect:在 GO Module 中,如果项目A直接引用项目B作为依赖,称为直接依赖,如果项目A引用项目B,而项目B又引用的项目C,项目A和项目C之间就称为间接依赖。直接依赖会用 indirect 直接标识出来
example/lib6 v3.2.0 // indirect
  • incompatible : 主版本2+模块会在模块路径中增加 /vN 后缀对于没有 go.mod 文件并且主版本2+的依赖会 +incompatible
example/lib6 v3.2.0+incompatible

当项目依赖的 package 中产生了版本冲突的问题时,go 会使用 mvs 算法选择一个最低的兼容版本。

依赖分发

image (10).png go 中的一个项目依赖会最终对应到 github/svn 等代码托管平台上的一个项目,如果直接使用版本管理仓库下载依赖存在以下问题:

  1. 无法保证构建稳定性
  2. 无法保证依赖可用性
  3. 增加了第三方代码托管平台的负载压力。

于是 go module 中就增加了 go proxy平台,他会缓存源站中的依赖内容,版本也不会更改,项目与直接在go proxy上拉取依赖。这样就可以保证软件的稳定性和可用性。

GOPROXYgo 中一个环境变量,它记录了proxy 平台的地址的列表,用逗号分隔,最后 direct 表示源站,下载依赖的时候会顺序访问服务列表中的 url,如果这些服务列表中都无法下载的需要的依赖,则会从源站中直接请求。

常用命令

go get

go get example.org/pkg @update # 默认升级版本
go get example.org/pkg @none # 删除依赖
go get example.org/pkg @v1.1.2 # 获取依赖的语音版本
go get example.org/pkg @23dfdd5 # 获取特定的commit伪版本
go get example.org/pkg @master # 获取依赖的主分支的最新commit

go mod

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

如果想要每个项目模块都能单独引入,那么可以在每个模块下面都加入 go.mod 文件。引入的时候直接引入模块地址即可。