Go Modules 模式


Go Modules 模式

GOPATH 目录

GOPATH 目录下一共包含三个子目录:

  • bin:存储所编译生成的二进制文件。
  • pkg:存储预编译的目标文件,以加快程序的后续编译速度。
  • src:存储所有 .go 文件或源代码。在编写 Go 应用程序,程序包和库时,一般会以 $GOPATH/src/github.com/foo/bar 的路径进行存放。

使用 go get 来拉取外部依赖时,会自动下载并安装到 $GOPATH 目录下。

go mod 命令

查看 go mod 都有哪些命令

# Go 的所有工具都可以使用 `go help` 来查看使用方法
go help mod

# 查看 go mod download 有哪些参数
go help mod download
命令作用
go mod help查看帮助信息
go mod init初始化当前文件夹,生成 go.mod 文件
go mod download下载 go.mod 文件中指明的所有依赖到本地(默认为 $GOPATH/pkg/mod 目录)增加 -x 参数 go mod download -x 会打印下载信息;go mod download -json 用来查看模块下载的 zip 存放位置,以及解压后的位置;
go mod tidy整理现有的依赖,执行时会把未使用的 module 移除掉,同时也会增加缺少的包
go mod graph查看现有的依赖结构图
go mod edit编辑 go.mod 文件,比如修改项目中使用的 go 版本 go mod edit -go=1.17
go mod vendor导出项目所有的依赖到 vendor 目录(需要执行 go build -mod=vendor 才可以使用 vendor 作为依赖来编译,但是在 v1.14 及以后的版本中,如果 golang 项目根目录下存在 vendor 目录,go build 命令会默认优先基于 vendor 目录缓存的三方依赖包构建 golang 程序,除非我们在 go build 命令后面加上 -mod=mod 参数)
go mod verify校验一个模块是否被篡改过,校验从 GOPROXY 服务器上下载的 zip 文件与 GOSUMDB 服务器下载下来的哈希值,是否匹配。
go mod why查看为什么需要依赖某模块,比如 go mod why gopkg.in/yaml.v2 gopkg.in/yaml.v3
go clean -modcache可以清空本地下载的 Go Modules 缓存 (会清空 $GOPATH/pkg/mod 目录)

go mod 环境变量

go mod 比较关联的几个环境变量


go env

GO111MODULE="auto"
GOPROXY="https://goproxy.cn,direct"
GOSUMDB="sum.golang.org"
GONOPROXY=""
GONOSUMDB=""
GOPRIVATE=""

GO111MODULE

Go 语言提供了 GO111MODULE 这个环境变量来作为 Go modules 的开关,其允许设置以下参数:

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

设置方式

go env -w GO111MODULE=on

也可以直接在 shell 环境变量中设置,比如我这里使用的是 Mac,且使用的 zsh,则在 vim ~/.zshrc 然后添加以下内容,如果是其它 Linux 系列系统,则需要在 ~/.bash_profile 文件中进行设置。


export GO111MODULE=on

然后要记得 source ~/.zshrc

GOPROXY

这个环境变量主要是用于设置 Go 模块代理(Go module proxy),其作用是用于使 Go 在后续拉取模块版本时直接通过镜像站点来快速拉取

GOPROXY 的默认值是:https://proxy.golang.org,direct


# 1. 七牛 CDN
go env -w  GOPROXY=https://goproxy.cn,direct

# 2. 阿里云
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

# 3. 官方
go env -w  GOPROXY=https://goproxy.io,direct

direct 是一个特殊指示符,用于指示 Go 在获取源码包时,先尝试在设置 GOPROXY 的地址下抓取,如果遇到 404 或 410 等错误时,再回溯到模块版本的源地址去抓取 (比如 GitHub 等)。

GOSUMDB

它的值是一个 Go checksum database,用于在拉取模块版本时(无论是从源站拉取还是通过 Go module proxy 拉取)保证拉取到的模块版本数据未经过篡改,若发现不一致,也就是可能存在篡改,将会立即终止。

GOSUMDB 的默认值为:sum.golang.org,在国内也是无法访问的,但是 GOSUMDB 可以被 Go 模块代理所代理,因此我们可以通过设置 GOPROXY 来解决,而先前我们所设置的模块代理 goproxy.cn (七牛云的 CDN)就能支持代理 sum.golang.org,所以这一个问题在设置 GOPROXY 后,可以不需要过度关心。

也可以将其设置为 off ,也就是禁止 Go 在后续操作中校验模块版本。但是不建议关闭校验。

GONOPROXY/GONOSUMDB/GOPRIVATE

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

这三个环境变量都是用在当前项目依赖了私有模块,例如自己公司部署的私有 git 仓库或者是 GitHub 中的私有仓库,都是需要进行设置的,否则会拉取失败。

设置 GOPRIVATE 之后,表示该地址为私有仓库,不会从 GOPROXY 所对应的地址上去下载。

而一般建议直接设置 GOPRIVATE,它的值将作为 GONOPROXY 和 GONOSUMDB 的默认值,所以建议直接设置 GOPRIVATE 即可。


# 以下表示 git.example.com 和 github.com/username/package 都是私有仓库,不会进行 GOPROXY 下载和校验
go env -w GOPRIVATE="git.example.com,github.com/username/package"
# 设置后,前缀为 `git.example.com` 和 `github.com/username/package` 的模块都会被认为是私有模块

# 表示所有模块路径为 example.com 的子域名都不进行 GOPROXY 下载和校验
# 需要注意的是不包括 example.com 本身
go env -w GOPRIVATE="*.example.com"

使用 Go Modules 初始化项目


# 开启 Go Modules 模块,保证 GO111MODULE=on
go env -w GO111MODULE=on

# 在任意文件夹下创建一个项目(不要求在 $GOPATH/src 目录下创建)
mkdir -p $HOME/modules_test

cd $HOME/modules_test

# 创建 go.mod 文件,同时起当前项目的模块名称
# 如果你是在 `$GOPATH/src` 目录下创建的文件夹可以直接执行 `go mod init` 命令初始化,不需要加模块名称
go mod init github.com/pudongping/moudles_test

# 在该项目下编写源代码,并下载依赖库
# 也可以不加 `-v` 参数
go get -v XXXXXXXX

eg: go get -v github.com/pudongping/moudles_test

下载的包其实被缓存在 $GOPATH/pkg/mod 目录和 $GOPATH/pkg/sumdb 目录下

  • go.mod 文件中:
  1. module:用于定义当前项目的模块路径。
  2. go:用于标识当前模块的 Go 语言版本,值为初始化模块时的版本。
  3. require:用于设置一个特定的模块版本。
  4. exclude:用于从使用中排除一个特定的模块版本。
  5. replace:用于将一个模块版本替换为另外一个模块版本。
# v0.0.0 表示版本信息
# 20190718012654 表示所拉取版本的 commit 时间
# fb15b899a751 表示所拉取版本的 commit 哈希值
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
  • go.sum 文件中:

go.sum 文件的作用是:罗列当前项目直接或间接依赖的所有模块版本,保证今后项目依赖的版本不会被篡改。间接依赖的包的哈希值也会被保存。

go.sum 文件中有两种 hash 的形式:

  1. h1:<hash> 将目标模块版本的 zip 文件开包后,针对所有包内文件依次进行 hash,然后再把它们的 hash 结果按照固定格式和算法组成总的 hash 值,如果不存在,表示可能依赖的库用不上
  2. xxx/go.mod h1.<hash> 表示 go.mod 文件做的 hash

# 将目标模块版本的 zip 文件开包后,针对包内所有文件依次进行 hash,然后再将 它们的 hash 结果汇总组成 hash
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=

# 针对 go.mod 文件的 hash 值
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=

go get 拉取命令

下载的模块会被放置于 $GOPATH/pkg/mod 目录中

命令作用
go get拉取依赖,会进行指定性拉取(更新),并不会更新所依赖的其它模块。(如果本地已存在要下载的包,将会直接使用本地已存在的包)
go get -u更新现有的依赖,会强制更新它所依赖的其它全部模块,不包括自身。
go get -u -t ./…更新所有直接依赖和间接依赖的模块版本,包括单元测试中用到的。
go get golang.org/x/text@latest拉取最新的版本,若存在 tag,则优先使用。
go get golang.org/x/text@master拉取 master 分支的最新 commit。
go get golang.org/x/text@v0.3.2拉取 tag 为 v0.3.2 的 commit。
go get golang.org/x/text@342b2e拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2。

go get 子参数说明

子命令描述
-d仅下载,不安装
-f和 -u 配合,强制更新,不检查是否过期
-t下载测试代码所需的依赖包
-u更新包,包括他们的依赖项
-v输出详细信息
insecure使用 http 等非安全协议

修改项目模块的版本依赖关系


go mod edit -replace=<老版本>=<需要替换的版本>

# 比如
go mod edit -replace=demo-package@v1.0.0=demo-package@v2.0.0

go list 命令以及参数

参数作用
-f用于查看对应依赖结构体中的指定的字段,其默认值就是 {{.ImportPath}},也就是导入路径,因此我们一般不需要进行调整
-json显示的格式,若不指定该选项,则会一行行输出。
-u显示能够升级的模块信息
-m显示当前项目所依赖的全部模块

比如查看 gin 框架的版本

go list -m -versions -json github.com/gin-gonic/gin

输出如下:

{
	"Path": "github.com/gin-gonic/gin",
	"Version": "v1.7.7",
	"Versions": [
		"v1.1.1",
		"v1.1.2",
		"v1.1.3",
		"v1.1.4",
		"v1.3.0",
		"v1.4.0",
		"v1.5.0",
		"v1.6.0",
		"v1.6.1",
		"v1.6.2",
		"v1.6.3",
		"v1.7.0",
		"v1.7.1",
		"v1.7.2",
		"v1.7.3",
		"v1.7.4",
		"v1.7.6",
		"v1.7.7"
	],
	"Time": "2021-11-24T13:54:13Z",
	"Dir": "/Users/pudongping/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7",
	"GoMod": "/Users/pudongping/go/pkg/mod/cache/download/github.com/gin-gonic/gin/@v/v1.7.7.mod",
	"GoVersion": "1.13"
}

私有库使用 Go Modules 时

  • 需要将 GOPRIVATE 环境变量设置成你私有库的域名
# GO111MODULE 设置成 on 或者 auto 都行
GO111MODULE="auto"
# GOPROXY 最好设置成国内镜像地址
GOPROXY="https://goproxy.cn,direct"
# GOPRIVATE 一定要设置成你的私有库域名,比如
GOPRIVATE="gitlab.xxx.com"
  • 然后执行以下命令即可。注意:前提是你能够通过 ssh 公钥拉取代码
# 以下假设我私有 git 仓库地址为 gitlab.xxx.com:2222
# 那么则需要调整为

cat << EOF >> ~/.gitconfig
[url "ssh://git@gitlab.xxx.com:2222"]
        insteadOf = https://gitlab.xxx.com
EOF

# 或者执行(效果都是一样的)
git config --global url."ssh://git@gitlab.xxx.com:2222".insteadof "https://gitlab.xxx.com"
  • 测试一下下载一个包
# 仅仅作为示范,此地址根本就不存在
go get -v gitlab.xxx.com/utils/arrayx

参考


文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
评论
  目录