Go 中的依赖管理与包管理工具 | 青训营笔记

131 阅读1分钟

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

Go 依赖管理的演化

GOPATH -> Go Vendor -> Go Module

GOPATH

Go 最原始的包管理机制, 通过环境变量 $GOPATH 指定一个工作区, 其目录结构为:

.
├── bin/ # 项目编译的二进制文件
├── pkg/ # 用以加速编译的中间产物
└── pkg/ # 项目源代码

项目所有依赖的源代码都会放到 src/ 文件夹下, go get 会下载最新版本的包到此文件夹下。

弊端

此方法的弊端是无法实现多版本控制。 例如 A、B 同时依赖于 C 的不同版本, $GOPATH 却只能保存其中一个版本。 如果 C 的更新做了破坏性的变更, 两者之间可能只有一个能编译成功。

Go Vendor

为了解决多版本的问题,Go Vendor 应运而生。 在bin项目的根目录下存在一个 vendor/ 文件夹, 此项目所有依赖包的副本都会放在这个文件夹下。 如:

├── READEME.md
├── man.go
└── vendor/ # 存放所有依赖的副本

依赖会以 vendor -> $GOPATH 的顺序寻找。

弊端

此方法的弊端是无法实现更深层次的多版本控制。 例如包 C 依赖包 A、B,而 A、B 又同时依赖于 D 的不同版本。 它仍旧依赖于项目源码,不能清晰标识使用的版本。

Go Module

1.11 实验性引入,1.16 默认开启。 它通过 go.mod 文件管理依赖包的版本。

Go Module 的依赖管理

依赖管理三要素

  1. 配置文件 描述依赖 对应 go.mod
  2. 中心仓库 存放依赖库 对应各种 Proxy
  3. 本地工具 对应 go getgo mod

典型的 go.mod 文件

module example/project/app

go 1.20

require(
    example/lib1 v1.0.2
    example/lib2 v2.3.0 // indirect
    example/lib3 v1.5.4+incompatible
)

版本号与版本管理规则

Go Module 使用定义了两种类型的版本规则, Go 会通过它计算最低的依赖版本。

  1. 语义化版本 ${MAJOR}.${MINOR}.${PATCH} 如 v1.2.3
  2. 基于 commmit 的伪版本 v0.0.0-yyyymmddhhmmss-${commit harsh} 如 v1.0.0-20201130134442-10cb98267c6c

require 单元中的关键字

  1. indirect

    example/lib2 v2.3.0 // indirect , 代表间接依赖。

    例如 A 依赖于 B,B 依赖于 C, A 对 C 就是间接依赖。

  2. incompatible

    example/lib3 v1.5.4+incompatible

    没有 go.mod (即没用使用 Go Module),会被以此标识。 代表可能存在不兼容的代码逻辑。

依赖分发与 Go Proxy

直接由第三方平台(如 GitHub)下载依赖存在以下问题

  • 无法保证作者不随意更改版本
  • 无法保证作者不删除代码仓库
  • 增加了第三方平台的访问压力

为了解决这些问题,Go 提供了 Proxy,它会缓存原站中的内容, 缓存过后的版本不会改变, 这样可以避免作者删除历史版本或者整个代码仓库, 保证依赖的稳定与可靠。

通过环境变量 $GOPROXY 指定代理,其值为以逗号分隔的 url 列表。 如 "https://proxy1.cn,https://proxy2.cn,https://proxy3.cn,direct" 。 其中, direct 标识原始站点。 这样前面的站点都没有包含依赖的情况下,Go 会直接从原站中下载依赖。 即依赖查找顺序是 proxy1 -> proxy2 -> direct。

管理工具

go get

典型的命令如:

go get example.org/pkg

其中 pkg 后可通过 @ 接特定值代表版本:

  • @update 默认,拉取 major 版本的最新提交
  • @none 删除依赖
  • @v1.1.2 标有 tag 的特定版本
  • @23dfdd5 特命的 commit
  • @master 分支的最新版本

go mod

典型的命令如:

go mod <subcommand>

subcommand 主要有三个:

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