Go依赖管理 | 青训营笔记

46 阅读2分钟

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

演进路线

背景:

  • 工程项目不可能基于标准库从 0 到 1 编码搭建。
  • 需要一种方式管理依赖包。

Go 依赖管理的演进:

  • GOPATH
  • Go Vendor
  • Go Module

GOPATH:

  • 在环境变量$GOPATH下有三个文件夹:

    • bin项目编译的二进制文件
    • pkg项目编译的中间产物,加速编译
    • src项目源码
  • 项目代码直接依赖src下的代码。

  • 通过go get命令下载最新版本的包到src目录下。

  • 弊端:无法实现依赖包的多版本控制。

Go Vendor:

  • 项目目录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor下。
  • 优先从项目目录下的vendor中寻找依赖,未找到再从GOPATH中找。
  • 通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的不同版本的冲突问题。
  • 弊端:无法控制依赖的版本;更新项目又可能出现依赖冲突,导致编译出错。

Go Module

终极目标:定义版本规则和管理项目依赖关系。

  • 通过go.mod文件管理依赖包版本(类似Java使用Maven进行依赖管理时的pom.xml)
  • 通过go getgo mod指令管理依赖包

完备的依赖管理的三要素:

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

配置文件

go.mod文件:

module example-app // 依赖管理基本单元, 声明模块名go 1.19 // 此模块的 go 标准库版本
​
require ( // 依赖库
    example/lib1 v1.0.2
    example/lib2 v1.0.0
    example/lib3 v0.1.0-20190725025543-5a5fe074e612
    example/lib4/v3 v3.0.2
    example/lib5/ v3.2.0+incompatible
)

对于每个依赖,都由依赖标识进行描述:[Module Path] [Version/Pseudo-Version] 关于Version

  • 语义化版本:${Major}.${Minor}.${Patch},例如1.3.0
  • 基于commit的伪版本,vX.X.X-yyyymmddhhmmss-abcdef123456,版本号-时间戳-十二位哈希码。
  • 主版本 2+ 的模块会在模块路径里增加 /vN 后缀。
  • 对于没有go.mod文件并且主版本 2+ 的依赖,会加+incompatible

依赖分发

主要解决依赖去哪里下载的问题。如果直接从GithubSVN等平台下载的话:

  • 无法保证构建稳定性,依赖作者可能增加/修改/删除版本。
  • 无法保证依赖可用性。
  • 增加代码托管平台的压力。

因此采用Proxy缓存源站的依赖,开发者从Proxy下载依赖,保证稳定可靠。 通过环境变量GOPROXY配置:

GOPROXY="https://proxy1.cn, https://proxy2.cn, direct"

配置服务站点 URL 列表,direct表示源站。下载依赖时会依次从各个 URL 站点查找,如果没找到再去下一个站点,最后到源站。

工具

使用go get命令工具拉取依赖。

go get example.org/pkg

在命令最后增加特定语句进行特定操作:

  • @update 默认,即拉取依赖。
  • @none 删除依赖。
  • @v1.1.2 tag版本,语义版本。
  • @23dfdd5 特定的commit。
  • @master 分支的最新commit。

使用go mod对项目进行依赖管理。

go mod init # 初始化项目
go mod download # 下载 go.mod 文件中指定的依赖到本地缓存
go mod tidy # 根据 go.mod 文件更新依赖(增加需要的依赖,删除不需要的依赖)