Go依赖管理的历史进程
GOPATH -> Go Vendor -> Go Module
GOPATH
简介
GOPATH是Go语言支持的一个环境变量$GOPATH
文件结构
- bin // 项目编译的二进制文件
- pkg //项目编译的中间产物,加速编译
- src //项目源码
项目代码直接依赖src下的代码
go get 下载最新版本的包到src目录下
弊端
无法实现package的多版本控制
Go Vendor
- 在项目目录下增加了vendor文件,所有依赖包的副本形式放在$ProjectRoot/vendor
- 依赖寻址方式:vendor =>GOPATH
结构
- README.md
- dao
- handler
- main.go
- service
- vendor
通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题
弊端
- 无法控制依赖版本
- 更新项目又可能出现依赖冲突,导致编译出错
Go Module
- 通过go.mod文件管理依赖包版本
- 通过go get/go mod指令工具管理依赖包
终极目标:定义版本规则和管理项目依赖关系
三要素
- go.mod:
配置文件,描述依赖 - Proxy:
中心仓库管理依赖库 - go get/mod:
本地工具
作用
- 定义当前模块的名称。
- 管理模块的依赖关系,包括依赖模块的名称和版本。
- 指定当前模块所使用的 Go 版本。
- 提供额外的配置选项,比如替换模块、限制依赖版本等。
经典结构
module example.com/myapp //依赖管理的基本单元
go 1.21 原生库
require ( // 单元依赖
github.com/gin-gonic/gin v1.9.0
github.com/stretchr/testify v1.8.4
)
replace github.com/old/module => github.com/new/module v1.2.0
各部分含义:
- model:
定义当前模块路径,通常是项目的根路径(可以是域名,也可以是本地路径) - go
指定当前模块所支持的go版本 - require:
列出模块的依赖项及其版本 - replace
用于替换模块依赖的源,可以指定另一个模块和路径 - exclude:
排除某些版本,防止他们被使用
例如:exclude github.com/buggy/module v1.0.0
依赖项的版本
Go模块管理中,依赖项的版本可以使用语义化版本或基于commit的伪版本
语义化版本
语义化版本简称SemVer是一种版本编号规则格式为
vMAJOR.MINOR.PATCH
- MAJOR(主版本号)
- 如果存在不向后兼容的更改,主版本号递增
- MINOR(次版本号)
- 如果增加了新功能但没有破坏兼容性,次版本号递增
- PATCH(修订号)
- 修复了漏洞或错误但没有改变接口时,修订号递增
优点:
- 清晰易读
- 自动兼容性管理,Go模块工具可以根据需求选择合适的版本
示例:
在go.mod文件中配置
require github.com/gin-gonic/gin v1.7.0
基于commit的伪版本(Pseudo-Version)
伪版本是基于Git提交信息生成的一个特殊的版本号,用在以下的场景替代正式的语义化版本:
1.模块尚未发布正式版本
2.需要引用特定的Git提交
3.需要在开发的过程中测试未发布的代码
格式:
vX.Y.Z-yyyymmddhhmmss-commit
-
vX.Y.Z
基于最后一个语义化版本号(如果没有发布,则为 v0.0.0)。 -
yyyymmddhhmmss
提交时间戳。 -
commit
Git 提交的哈希值前 12 位。
生成规则:
1.如果当前分支未打 tag,则伪版本基于 v0.0.0。
2.如果当前分支有 tag,则伪版本基于最新的 tag 版本号。
3.时间戳和提交哈希确保伪版本唯一。
优点:
1.能够引用尚未发布的模块代码。
2.精确定位到某个提交,适合调试和开发测试。
假设模块的最新提交哈希为 abc123456789,提交时间为 2024 年 1 月 1 日 12:00,则对应的伪版本可能为:
v1.5.3-20240101120000-abc123456789
在 go.mod 文件中配置:
require github.com/example/module v1.5.3-20240101120000-abc123456789
依赖配置
indirect
indirect 是 go.mod 文件中依赖项的一种标识,用于说明某个模块是间接依赖
间接依赖是指一个模块不是直接被你的代码引用,而是通过你的某个直接依赖引入的。
间接依赖在go.mod中的显示
require (
github.com/example/moduleA v1.2.3
github.com/example/moduleB v1.4.5 // indirect
)
其中 // indirect 标识 B 是通过直接依赖 A 间接引入的。
如果你的代码需要引用某个间接依赖模块的功能,则需要明确声明该依赖。在这种情况下:
Go 会将间接依赖升级为直接依赖(去掉 indirect 标识)。
作用:
使用 indirect 标识可以清晰区分模块的依赖关系,便于维护构建系统的稳定性和最小化依赖树。
间接依赖是 Go 自动计算并写入 go.mod 文件的。通常会在以下情况下生成:
- 运行 go mod tidy 时,Go 会清理未使用的依赖并添加需要的间接依赖。
- 运行 go get 安装或升级依赖时,间接依赖可能会显式写入 go.mod 文件。
incompatible
incompatible 标记表示模块的版本(v2+)与 Go 模块系统的版本规范不完全兼容。
- 主要出现在旧版模块未更新模块路径的情况下。
- 可以正常使用,但可能带来版本冲突和升级问题。
从 v2 开始,模块路径应包括版本号后缀(如 /v2)
GOPROXY
简介
Go Proxy 是一个模块代理服务器,用于加速和简化 Go 模块的分发。它位于 Go 工具和模块源代码之间,负责:
- 缓存模块版本。
- 提供稳定的依赖下载服务。
- 避免开发者直接访问原始代码仓库(如 GitHub、GitLab)。
- 通过 Proxy,Go 工具可以快速、安全地获取所需的模块依赖。
当你在 Go 环境中启用了模块(GO111MODULE=on),Go 工具会默认通过 https://proxy.golang.org 获取依赖。
优点
- 提高模块获取速度。
- 避免依赖源码仓库的可用性。
- 缓存机制减少重复下载。
- 支持验证模块的完整性和安全性。
缺点
- 对私有模块支持有限(大部分 Proxy 不支持)。
- 可能需要配置额外的代理服务(如国内用户访问 proxy.golang.org 不稳定)。
工具
go mod
go mod
- init // 初始化,创建go.mod项目
- download // 下载模块到本地缓存
- tidy // 增加需要的依赖,删除不需要的依赖
示例
go mod init example.com/myproject
go mod tidy
go get example.com/mymodule@v1.2.3
go get
go get 是一个用于获取远程代码包的命令,支持以下操作:
- 下载代码包到模块缓存中。
- 更新项目的依赖版本。
- 自动修改 go.mod 文件中的依赖项。
- 在传统的 $GOPATH 模式下,获取包并放到 $GOPATH/src 下。
go get example.org/pkg
- @update // 默认
- @none // 删除依赖
- @v1.1.2 // tag版本,语义版本
- @23dfdd5 // 特定的commit
- @master // 分支的最新commit