go.mod
go.mod 示例:
1 | module api.local |
- module:用于定义当前项目的模块路径。
- go:用于设置预期的 Go 版本。
- require:用于设置一个特定的模块版本。
- exclude:用于从使用中排除一个特定的模块版本。
- replace:用于将一个模块版本替换为另外一个模块版本。
环境变量
GO111MODULE
Go modules 的开关,auto/on/off,推荐 on
1.17 中将完全放弃对 GOPATH 模式的支持,忽略 GO111MODULE 变量设置。
GOPROXY
设置 go 模块代理,用于使 Go 在后续拉取模块版本时能够脱离传统的 VCS 方式从镜像站点快速拉取。它拥有一个默认:https://proxy.golang.org,direct
,但很可惜proxy.golang.org
在中国无法访问,故而建议使用goproxy.cn
作为替代:https://goproxy.cn,direct
“direct” 为特殊指示符,用于指示 Go 回源到模块版本的源地址去抓取(比如 GitHub 等),当值列表中上一个 Go module proxy 返回 404 或 410 错误时,Go 自动尝试列表中的下一个,遇见 “direct” 时回源,遇见 EOF 时终止并抛出类似 “invalid version: unknown revision…” 的错误。
GOSUMDB
Go checksum database,用于使 Go 在拉取模块版本时(无论是从源站拉取还是通过 Go module proxy 拉取)保证拉取到的模块版本数据未经篡改,也可以是“off”即禁止 Go 在后续操作中校验模块版本
默认:sum.golang.org
,但是在国内无法访问,故而更加建议将 GOPROXY 设置为goproxy.cn
,因为goproxy.cn
支持代理sum.golang.org
GONOPROXY / GONOSUMDB / GOPRIVATE
这三个环境变量都是用在当前项目依赖了私有模块,也就是依赖了由 GOPROXY 指定的 Go module proxy 或由 GOSUMDB 指定 Go checksum database 无法访问到的模块时的场景。
其中 GOPRIVATE 较为特殊,它的值将作为 GONOPROXY 和 GONOSUMDB 的默认值,所以建议的最佳姿势是只是用 GOPRIVATE。
示例:
1 | GOPRIVATE=*.corp.example.com |
表示所有模块路径以corp.example.com
的下一级域名(如:team1.corp.example.com
)为前缀的模块版本都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括corp.example.com
本身。
Global Caching
这个主要是针对 Go modules 的全局缓存数据说明,如下:
- 同一个模块版本的数据只缓存一份,所有其他模块共享使用。
- 目前所有模块版本数据均缓存在
$GOPATH/pkg/mod
和$GOPATH/pkg/sum
下,未来或将移至$GOCACHE/mod
和$GOCACHE/sum
下 - 可以使用
go clean -modcache
清理所有已缓存的模块版本数据
indirect 表示什么意思
https://my.oschina.net/renhc/blog/3162751
在执行命令 go mod tidy
时,Go module 会自动整理 go.mod 文件
,如果有必要会在部分依赖包的后面增加 // indirect
注释。一般而言,被添加注释的包肯定是间接依赖的包,而没有添加 // indirect
注释的包则是直接依赖的包,即明确的出现在某个 import
语句中。
然而,这里需要着重强调的是:并不是所有的间接依赖都会出现在 go.mod
文件中。
间接依赖出现在 go.mod
文件的情况,可能符合下面所列场景的一种或多种:
- 直接依赖未启用 Go module
- 直接依赖 go.mod 文件中缺失部分依赖
直接依赖未启用 Go module
如下图所示,Module A 依赖 B,但是 B 还未切换成 Module,也即没有 go.mod
文件,此时,当使用 go mod tidy
命令更新 A 的 go.mod
文件时,B 的两个依赖 B1 和 B2 将会被添加到 A 的 go.mod
文件中(前提是 A 之前没有依赖 B1 和 B2),并且 B1 和 B2 还会被添加 // indirect
的注释。
此时 Module A 的 go.mod
文件中 require 部分将会变成:
1 | require ( |
依赖 B 及 B 的依赖 B1 和 B2 都会出现在 go.mod
文件中。
直接依赖 go.mod 文件不完整
如上面所述,如果依赖 B 没有 go.mod
文件,则 Module A 将会把 B 的所有依赖记录到 A 的 go.mod
文件中。即便 B 拥有 go.mod
,如果 go.mod
文件不完整的话,Module A 依然会记录部分 B 的依赖到 go.mod
文件中。
如下图所示,Module B 虽然提供了 go.mod
文件中,但 go.mod
文件中只添加了依赖 B1,那么此时 A 在引用 B 时,则会在 A 的 go.mod
文件中添加 B2 作为间接依赖,B1 则不会出现在 A 的 go.mod
文件中。
此时 Module A 的 go.mod
文件中 require 部分将会变成:
1 | require ( |
由于 B1 已经包含进 B 的 go.mod
文件中,A 的 go.mod
文件则不必再记录,只会记录缺失的 B2。
总结
为什么要记录间接依赖
在上面的例子中,如果某个依赖 B 没有 go.mod
文件,在 A 的 go.mod
文件中已经记录了依赖 B 及其版本号,为什么还要增加间接依赖呢?
我们知道 Go module 需要精确地记录软件的依赖情况,虽然此处记录了依赖 B 的版本号,但 B 的依赖情况没有记录下来,所以如果 B 的 go.mod
文件缺失了(或没有)这个信息,则需要在 A 的 go.mod
文件中记录下来。此时间接依赖的版本号将会跟据 Go module 的版本选择机制确定一个最优版本。
如何处理间接依赖
综上所述间接依赖出现在 go.mod
中,可以一定程度上说明依赖有瑕疵,要么是其不支持 Go module,要么是其 go.mod
文件不完整。
由于 Go 语言从 v1.11 版本才推出 module 的特性,众多开源软件迁移到 go module 还需要一段时间,在过渡期必然会出现间接依赖,但随着时间的推进,在 go.mod
中出现 // indirect
的机率会越来越低。
出现间接依赖可能意味着你在使用过时的软件,如果有精力的话还是推荐尽快消除间接依赖。可以通过使用依赖的新版本或者替换依赖的方式消除间接依赖。
如何查找间接依赖来源
Go module 提供了 go mod why
命令来解释为什么会依赖某个软件包,若要查看 go.mod
中某个间接依赖是被哪个依赖引入的,可以使用命令 go mod why -m <pkg>
来查看。
另外,命令 go mod why -m all
则可以分析所有依赖的依赖链。
如何锁定版本号
使用 replace