Go Modules 是 Go 语言的代码依赖管理工具。类似于 PHP 中的 Composer、Node.js 中的 npm 。
Go Modules 由官方维护。自 Go 版本 1.14 开始,官方鼓励所有用户迁移到 Go Modules 以进行依赖项管理。

为什么使用Go Modules

1、解决 GOPATH 的问题
5.依赖管理 - 图1GOPATH/src` 下,抛弃 $GOPATH 的好处,是你能在任意地方创建的 Go 项目。
2、进行版本管理

使用

1. 初始化

新项目,我们可以使用 go mod init 初始化生成 go.mod 文件:

  1. $ go mod init

2. Go Proxy

因国内访问外网受限,一般我们都会配合 Go Proxy 使用,以防止使用 go get 获取源码包时花费时间过长或无法下载:

  1. $ go env -w GOPROXY=https://goproxy.cn

知识点: 我们使用 go env -w 来修改 Go 相关的环境变量。

Go Proxy 设置完成后即可放心使用 go get 来下载依赖了,作为测试,我们下载 HttpRouter :

  1. $ go get github.com/julienschmidt/httprouter

使用 Proxy 的情况下一般很快就能下载完成。
安装 package 的原则是先拉最新的 release tag,若无 tag 则拉最新的 commit。

3. go.mod

每一次的 go get 都会同时修改 go.modgo.sum 文件。
查看go.mode

  1. module github.com/junetalk/goblog
  2. go 1.17
  3. require github.com/gorilla/mux v1.8.0

几个参数:

  • module:我们的 goblog 在 Go Module 里也算是一个 Module ;
  • go:指定了版本要求,最低 1.17
  • require:是项目所需依赖

    4. go.sum

    go.sum 文件保存着依赖包的版本和哈希值:
    go.sum
    1. github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
    2. github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=

    go.sum 里不仅会保存直接依赖包的哈希值,间接依赖包的哈希值也会被保存。

接下来下载 gin

  1. $ go get github.com/gin-gonic/gin

下载成功后打开 go.sum ,会发现里面的内容远远多于 go.mod。这是因为 gin 有很多依赖包,而这些依赖包也会有自己的依赖。
每个模块路径有如下两种哈希:

  1. github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
  2. github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=

前者为 Go Modules 打包整个模块包文件 zip 后再进行 hash 值,而后者为针对 go.mod 的 hash 值。

5.indirect

查看go.mod 中,可以看到 require 区块里有 // indirect 字样:

  1. module github.com/junetalk/goblog
  2. go 1.17
  3. require (
  4. github.com/gin-contrib/sse v0.1.0 // indirect
  5. github.com/gin-gonic/gin v1.7.4 // indirect
  6. github.com/go-playground/locales v0.13.0 // indirect
  7. github.com/go-playground/universal-translator v0.17.0 // indirect
  8. github.com/go-playground/validator/v10 v10.4.1 // indirect
  9. github.com/golang/protobuf v1.3.3 // indirect
  10. github.com/gorilla/mux v1.8.0 // indirect
  11. github.com/json-iterator/go v1.1.9 // indirect
  12. github.com/leodido/go-urn v1.2.0 // indirect
  13. github.com/mattn/go-isatty v0.0.12 // indirect
  14. github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
  15. github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
  16. github.com/ugorji/go/codec v1.1.7 // indirect
  17. golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
  18. golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect
  19. gopkg.in/yaml.v2 v2.2.8 // indirect
  20. )

此标志标明这个依赖包还未被使用,如果你在代码的某个地方 import 到的话,VSCode 的 Go 插件就会自动将这个标志去除。

6. go mod tidy 命令

此命令做整理依赖使用,执行时会把未使用的 module 移除掉,我们试试:

  1. $ go mod tidy

再次查看 go.modgo.sum 文件,会发现我们上面测试引入的两个包,因未使用,皆被移除。

7. 源码包的存放位置

默认源码包存放于 $GOPATH/pkg/mod

  1. $ ll $GOPATH/pkg/mod
  2. drwxr-xr-x 5 junetalk staff 160B 9 1 18:53 cache
  3. drwxr-xr-x 23 junetalk staff 736B 9 2 15:05 github.com
  4. drwxr-xr-x 3 junetalk staff 96B 9 1 17:32 golang.org
  5. drwxr-xr-x 3 junetalk staff 96B 9 2 15:05 gopkg.in
  6. drwxr-xr-x 3 junetalk staff 96B 9 1 18:53 honnef.co
  7. drwxr-xr-x 4 junetalk staff 128B 9 1 18:53 mvdan.cc

8. 清空 Go Modules 缓存

使用以下命令可以清空本地下载的 Go Modules 缓存:

  1. $ go clean -modcache

9. 下载依赖

默认情况下,当 go rungo build 命令执行时,Go 会基于自动 go.mod 文件自动拉取依赖。
Go Module 也提供了一个命令用以下载项目所需依赖:

  1. $ go mod download

10. 所有 Go Modules 命令

以下是完整的命令列表,有些不常用的篇幅原因我们不做讲解:

命令 作用
go mod init 生成 go.mod 文件
go mod download 下载 go.mod 文件中指明的所有依赖
go mod tidy 整理现有的依赖
go mod graph 查看现有的依赖结构
go mod edit 编辑 go.mod 文件
go mod vendor 导出项目所有的依赖到 vendor 目录
go mod verify 校验一个模块是否被篡改过
go mod why 查看为什么需要依赖某模块

11. 相关环境变量

1). GO111MODULE

此变量为 Go modules 的开关,此值有以下几个可能:

  • auto:项目包含了 go.mod 文件的话启用 Go modules,目前在 Go1.11 至 Go1.15 中仍然是默认值。
  • on:启用 Go modules,推荐设置,将会是未来版本中的默认值。
  • off:禁用 Go modules,不推荐设置。

因是在 Go1.11 版本添加,故命名为 GO111MODULE。
未来 GO111MODULE 会先调整为默认值为 on(曾经在 Go1.13 想想改为 on,并且已经合并了 PR,但最后因为种种原因改回了 auto),然后再把 GO111MODULE 这个变量去掉,目前猜测会在 Go 2。太早去掉 GO111MODULE 的支持,会存在兼容性问题。

2). GOPROXY

此变量用于设置 Go 模块代理(Go module proxy),其作用是拉取源码时能够脱离传统的 VCS 方式,直接通过镜像站点来快速拉取。
镜像的好处多多,一个是防止某个版本的代码被有意或无意删除。第二是能将源码压为 zip 包,方便传输。最重要的 —— 可以做镜像加速站点,这在例如国内这种不稳定的网络环境下尤为重要。
GOPROXY 的默认值是:

  1. https://proxy.golang.org,direct

然而 proxy.golang.org 在国内是无法访问的,所以我们使用 Go modules 时,需设置国内的 Go 模块代理:

  1. $ go env -w GOPROXY=https://goproxy.cn,direct

信息: goproxy.cn 是一个由 CDN 提供商七牛云支持的非营利性项目。七牛云也是中国最早在生产环境中使用 Go 的公司之一,项目介绍请见:github.com/goproxy/goproxy.cn/blob…

GOPROXY 的值是一个以英文逗号 , 分割的 Go 模块代理列表,可设置多个模块代理。将其设置为 off ,将会禁止 Go 在后续操作中使用任何 Go 模块代理。
direct 标志
direct 标志意味着从源地址抓取(比如 GitHub 等)。
如我们设置 GOPROXY 的值为:

  1. https://goproxy.cn,direct

则告诉 go get 在获取源码包时先尝试 [https://goproxy.cn](https://goproxy.cn),如果遇到 404 等错误时,再尝试从源地址抓取。

3). GOSUMDB

此值是 Go Checksum Database 的缩写,用于在拉取模块版本时(无论是从源站拉取还是通过 Go Module Proxy 拉取)保证拉取到的模块代码包未经过篡改,若发现不一致将会立即中止。
GOSUMDB 的默认值为:

  1. sum.golang.org

在国内同样无法访问,所幸 GOSUMDB 可以被 Go Module Proxy 代理。我们所设置的模块代理 goproxy.cn 支持代理 sum.golang.org
另外,此变量还可设置为 off,会禁止 Go 在后续操作中校验模块哈希。

4). GONOPROXY/GONOSUMDB/GOPRIVATE

这三个环境变量都是用在依赖了私有模块,这些模块 GOPROXY 和 GOSUMDB 都无法读取。

  • GONOPROXY —— 设置不走 Go Proxy 的 URL 规则;
  • GONOSUMDB —— 设置不检查哈希的 URL 规则;
  • GOPRIVATE —— 设置私有模块的 URL 规则,会同时设置以上两个变量。

因为 GOPRIVATE 会同时设定以上两个,所以一般私有仓库使用 GOPRIVATE 即可。
以上三个值,都可使用逗号分隔来设置多个选项。例如:

  1. $ go env -w GOPRIVATE="git.example.com,github.com/name/project"

设置后当 go get 时,前缀为 git.example.comgithub.com/name/project 的模块都会被认为是私有模块。
我们也可以利用通配符,例如:

  1. $ go env -w GOPRIVATE="*.example.com"

这样子设置的话,所有模块路径为 example.com 的子域名(例如:git.example.com)都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 example.com 本身。