原创分享 Go module 基础使用以及在 Go 1.16 版本中的改进

yudotyang · 2021年02月25日 · 148 次阅读

Go 1.11 开始对模块进行支持,主要目的就是使用模块来管理依赖。本文介绍使用 modules 的一些基本操作。在 Go 1.16 中,GO111MODULE 默认是开启状态。在 Go 1.16 版本以下,请确保你已经设置了 GO111MODULE=on 状态。

一个 module 就是一组包的集合,即 go.mod 文件所在目录下定义的所有的包都属于这个模块。go.mod 文件定义了模块的路径 path,这个路径是用于 import 的路径以及编译时该模块依赖于其他模块的需求。该模块依赖的模块通过模块路径 + 语义化的版本号的格式添加到 go.mod 中。

本文介绍以下 module 的操作:

  • 在项目下创建新的 module
  • 添加依赖
  • 升级依赖 ++ 升级依赖的次版本 ++ 升级依赖到主版本
  • 移除不用的依赖

创建 module

你可以在 $GOPATH/src 之外创建新的项目

go mod init modulename 命令可以创建一个空的 go.mod 文件。如下:

module example.com/demo

go.mod 文件在项目根目录下创建一次即可,如果该根目录下再有子目录,那么子目录下就不再需要重复创建 go.mod 文件。因为所有子目录中的包都同属于该模块。该模块中的包在被导入的时候,import 的路径使用 module + / + package 的模式导入即可。

官网有消息称在 Go 1.17 版本将不再对 GOPATH 模式支持。所有,如果有基于非 MODULE 模式编译的老项目,建议尽快迁移到 MODULE 模式下。

添加依赖

通过在程序文件中 import 对应的包,在 go 1.16 之前的版本中,运行 go 命令(如 go run, go build , go test)时, go 会通过以下规则自动解析并下载包: 1、添加特定版本的包:要 import 的包在 go.mod 文件中有对应的的 require 描述,则按对应描述的版本下载。 2、添加最新版本的包:要 import 的包 在 go.mod 中没有 require 描述,则按最新版本下载该包,同时将该包加入到 go.mod 中。

在 go 1.16 版本中,运行 go 命令(go run, go build,go test)时,如果 import 的依赖在 go.mod 文件中没有,不会再自动下载并修改 go.mod 和 go.sum 文件,而会提示错误,并需要手动执行 go get 命令下载对应的包。原因是自动修复的方式不是在任何场景下都适用:如果导入的包在没有提供任何依赖的情况自动添加新依赖,则可能会引起公共依赖包的升级等。

通过运行 *go get ./ ... * 可以自动查找并下载所有的包

添加完包后,可以通过使用 go list -m all 查看当前模块所依赖的包列表。

在 go.mod 所在根目录下,除了维护 go.mod 文件外,还有一个 go.sum 文件。go.sum 文件是对导入的依赖包的特定版本的 hash 校验值,作用就是确保将来下载的依赖包版本和第一次下载到的依赖版的版本号相同,以防止在将来有版本号升级后 程序不兼容的问题。所以,go.mod 和 go.sum 文件都需要被加入版本管理中。 如下:

$ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO...
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq...
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3...
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX...
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/Q...
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9...
$

私有包源码下载的安全约束 -- GOVCS 环境变量

go command 下载依赖包的来源有两个:分别是代理网站(如 proxy.golang.org) 和版本控制仓库(例如 github.com 等源码仓库管理站)。尤其是当依赖包是私有包,未在代理网站上传时,从版本控制仓库下载源代码尤为重要。 但这也涉及到一个安全问题:恶意服务器可能会利用版本控制工具中的 bug 来运行错误代码。

因此,Golang 通过 GOVCS 环境变量的配置来确保安全。格式如下: GOVCS=<module prefix>:<tool name>,[<module prefix>:<tool name>,...] 例如:GOVCS=github.com:git,evil.com:off,*:git|hg 代表可以使用 git 命令下载包路径是 github.com 的依赖包。任何版本控制命令都不可以下载 evil.com 路径的包。其余的任何路径的包都可以使用 git 或 hg 命令下载。

升级依赖

在 go module 中,使用语义化的版本号来标记所依赖的包的版本。 一个语义化的版本号有三部分组成:主版本,次版本和补丁版本号。例如 v0.1.2, 代表主版本号是 0,次版本号是 1,补丁版本号是 2 。 另外,以下版本号格式也都是合法的:注意,伪版本号在 Go1.13 版本之后 就已不再支持。

1. gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
2. gopkg.in/vmihailenco/msgpack.v2 v2.9.1
3. gopkg.in/yaml.v2 <=v2.2.1
4. github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
5. latest

次版本升级

  • 运行 go get -u 将会升级到最新的次要版本或者修订版本 (x.y.z, z 是修订版本号, y 是次要版本号)
  • 运行 go get -u=patch 将会升级到最新的修订版本

主版本升级

Go module 规定包的不同主要版本号需要使用不同的 module 路径。例如一个包 example.com/demo 的后续主版本应该使用以下路径 example.com/demo/vX, 即 v2 版本的路径是 example.com/demo/v2, v3,v4...依次类推。

所以,要升级依赖的主版本则通过以下方式:

  • 在程序文件中 import 包的特定版本。例如 import demoV2 “example.com/demo/v2”
  • 使用 go get package@version

移除依赖

如果我们不再使用一个依赖包,则我们从代码文件的 import 中移除。但在 go.mod 文件的 require 中并不会自动移除。需要使用go get tidy 命令将其从 go.mod 文件中移除

因为 go.mod 的作用是在构建包的时候(例如 go build 或 go test),告诉编译器需要哪些包,哪些包是缺失的,而不是告诉编译器什么时候该删除包。*所以,想要安全的移除包需使用 命令 go get tidy *

总结

本文主要介绍了使用 go module 的流程:

  • go mod init 创建一个新模块,初始化 go.mod 文件
  • go 1.16 版本之前,go build, go test 以及其他包构建命令 自动查找并下载依赖包并将依赖添加到 go.mod 中,go 1.16 及其以后,go command 不再自动下载不在 go.mod 中的依赖,而是需要通过 go get 命令手动下载
  • go list -m all 打印出当前模块直接依赖的列表
  • go get 升级依赖或添加新依赖。其中 go get 只能升级次版本。主版本的升级需要按主版本规则 import 后,再 go get package@version
  • go mod tidy 从 go.mod 中移除不需要的依赖
  • go 1.17 计划移除 GOPATH 模式,建议尽快将非 module-aware 模式的老项目迁移到 module-aware 模式
更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册