1.10 GO 依赖管理工具 go Modules(官方推荐)

以前写过一篇关于go管理依赖包工具 dep的文章,当时认为dep将会成为官方依赖工具,现在看来是自己图样图斯内幕破了,正如官方一直提到dep是“official experiment”官方实验项目的那样,随着go modules 在go1.11版推出,go1.12版功能不断改进,再到go1.13版完善优化,正式扶正。预计dep将来也只能定格在“official experiment”了。

Go Modules有哪些特点:

  • Go Modules是官方正式推出的包依赖管理项目,由Russ Cox (即Go 现在的掌舵人)推动,dep是“official experiment”仅此而已。

  • Go modules 出现的目的之一就是为了解决 GOPATH 的问题,也就相当于是抛弃 GOPATH 了。以前项目必须在$GOPATH/src 里进行,现在Go 允许在$GOPATH/src外的任何目录下使用 go.mod 创建项目。当然现在

  • 随着模块一起推出的还有模块代理协议(Module proxy protocol),通过这个协议我们可以实现 Go 模块代理(Go module proxy),也就是依赖镜像。

  • Tag必须遵循语义化版本控制,如果没有将忽略 Tag,然后根据你的 Commit 时间和哈希值再为你生成一个假定的符合语义化版本控制的版本号。

  • Go modules 还默认认为,只要你的主版本号不变,那这个模块版本肯定就不包含 Breaking changes,因为语义化版本控制就是这么规定的啊。

  • Global Caching 这个主要是针对 Go modules 的全局缓存数据说明,如下:

    • 同一个模块版本的数据只缓存一份,所有其他模块共享使用。
    • 目前所有模块版本数据均缓存在 $GOPATH/pkg/mod$GOPATH/pkg/sum 下,未来或将移至 $GOCACHE/mod$GOCACHE/sum 下( 可能会在当 $GOPATH 被淘汰后)。
    • 可以使用 go clean -modcache 清理所有已缓存的模块版本数据。

    另外在 Go1.11 之后 GOCACHE 已经不允许设置为 off 了,我想着这也是为了模块数据缓存移动位置做准备,因此大家应该尽快做好适配。

如果你的版本是go1.12或更早版本,这里建议升级到go1.13,来体验一把go modules,看它能给你带来哪些方面身心的愉悦。

本文将介绍使用Go Modules相关操作

1、安装Go 1.13或升级到Go 1.13

安装

2、配置环境变量

  1. #修改 GOBIN 路径(可选)
  2. go env -w GOBIN=$HOME/bin
  3. #打开 Go modules
  4. go env -w GO111MODULE=on
  5. #设置 GOPROXY
  6. go env -w GOPROXY=https://goproxy.cn,direct

go env -w: Go1.13 新增了 go env -w 用于写入环境变量,而写入的地方是 os.UserConfigDir 所返回的路径,需要注意的是 go env -w 不会覆写。需要指出,它不会覆盖系统环境变量。

GO111MODULE:

这个环境变量主要是 Go modules 的开关,主要有以下参数:

  • auto:只在项目包含了 go.mod 文件时启用 Go modules,在 Go 1.13 中仍然是默认值,详见 :golang.org/issue/31857。
  • on:无脑启用 Go modules,推荐设置,未来版本中的默认值,让 GOPATH 从此成为历史。
  • off:禁用 Go modules。

GOPROXY:

这个环境变量主要是用于设置 Go 模块代理,它的值是一个以英文逗号 “,” 分割的 Go module proxy 列表,默认是proxy.golang.org,国内访问不了。这里要感谢盛傲飞和七牛云为中国乃至全世界的 Go 语言开发者提供免费、可靠的、持续在线的且经过CDN加速Go module proxy(goproxy.cn)。

其实值列表中的 “direct” 为特殊指示符,用于指示 Go 回源到模块版本的源地址去抓取(比如 GitHub 等),当值列表中上一个 Go module proxy 返回 404 或 410 错误时,Go 自动尝试列表中的下一个,遇见 “direct” 时回源,遇见 EOF 时终止并抛出类似 “invalid version: unknown revision…” 的错误。

3、创建你的项目

这里我们在$GOPATH/src外,创建 /var/www/demo实例

  1. mkdir /var/www/demo
  2. cd /var/www/demo

新建main.go

  1. package main
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "fmt"
  5. )
  6. func main() {
  7. r := gin.Default()
  8. r.GET("/ping", func(c *gin.Context) {
  9. fmt.Println("hello world!")
  10. c.JSON(200, gin.H{
  11. "message": "pong",
  12. })
  13. })
  14. r.Run() // listen and serve on 0.0.0.0:8080
  15. }

4、在/var/www/demo根目录下

  1. #生成go.mod文件
  2. go mod init demo

打开go.mod文件,内容

  1. module demo
  2. go 1.13

go.mod 是启用了 Go moduels 的项目所必须的最重要的文件,它描述了当前项目(也就是当前模块)的元信息,每一行都以一个动词开头,目前有以下 5 个动词:

  • module:用于定义当前项目的模块路径。
  • go:用于设置预期的 Go 版本。
  • require:用于设置一个特定的模块版本。
  • exclude:用于从使用中排除一个特定的模块版本。
  • replace:用于将一个模块版本替换为另外一个模块版本。

这里的填写格式基本为包引用路径+版本号,另外比较特殊的是 go $version,目前从 Go1.13 的代码里来看,还只是个标识作用,暂时未知未来是否有更大的作用。

5、在/var/www/demo根目录下,执行 go build

  1. go build

完成后项目

  1. ├── demo
  2. ├── go.mod
  3. ├── go.sum
  4. └── main.go

项目中增加了go.sum、demo文件

go.sum文件内容

  1. github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
  2. github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
  3. github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod
  4. github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
  5. github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
  6. ...

go.sum类似于比如 dep 的 Gopkg.lock 的一类文件,它详细罗列了当前项目直接或间接依赖的所有模块版本,并写明了那些模块版本的 SHA-256 哈希值以备 Go在今后的操作中保证项目所依赖的那些模块版本不会被篡改。

我们可以看到一个模块路径可能有如下两种:

  1. github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
  2. github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=

前者为 Go modules 打包整个模块包文件 zip 后再进行 hash 值,而后者为针对 go.mod 的 hash 值。他们两者,要不就是同时存在,要不就是只存在 go.mod hash。

那什么情况下会不存在 zip hash 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的 zip hash,就会出现不存在 zip hash,只存在 go.mod hash 的情况。

go.mod文件内容发生了变化,增加了

  1. require github.com/gin-gonic/gin v1.4.0

默认使用最新版本的package。

更换依赖版本

查看gin所有历史版本

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

github.com/gin-gonic/gin v1.1.1 v1.1.2 v1.1.3 v1.1.4 v1.3.0 v1.4.0

如果想更换依赖版本,比如v1.3.0,怎么办?

只需执行如下命令

  1. go mod edit -require="github.com/gin-gonic/gin@v1.3.0"
  2. go tidy #更新现有依赖

@后跟版本号,这个时候go.mod已经修改好了

  1. require github.com/gin-gonic/gin v1.3.0

查看所有项目依赖的包

  1. go list -m all
  1. github.com/davecgh/go-spew v1.1.0
  2. github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
  3. github.com/gin-gonic/gin v1.4.0
  4. github.com/golang/protobuf v1.3.1
  5. github.com/json-iterator/go v1.1.6
  6. github.com/mattn/go-isatty v0.0.7
  7. github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
  8. github.com/modern-go/reflect2 v1.0.1
  9. github.com/pmezard/go-difflib v1.0.0
  10. github.com/stretchr/objx v0.1.0
  11. github.com/stretchr/testify v1.3.0
  12. github.com/ugorji/go v1.1.4
  13. golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
  14. golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c
  15. golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
  16. golang.org/x/text v0.3.0
  17. gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
  18. gopkg.in/go-playground/assert.v1 v1.2.1
  19. gopkg.in/go-playground/validator.v8 v8.18.2
  20. gopkg.in/yaml.v2 v2.2.2

注意

使用go.mod管理依赖会对go get命令产生一定影响,

  • go help module-getgo help gopath-get分别去了解 Go modules 启用和未启用两种状态下的 go get 的行为

  • go get 拉取新的依赖

    • 拉取最新的版本(优先择取 tag):go get golang.org/x/text@latest

    • 拉取 master 分支的最新 commit:go get golang.org/x/text@master

    • 拉取 tag 为 v0.3.2 的 commit:go get golang.org/x/text@v0.3.2

    • 拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2:go get golang.org/x/text@342b2e

    • go get -u 更新现有的依赖

快速迁移项目至 Go Modules

  1. 在你项目的根目录下执行 go mod init 项目名 (项目名可不加),以生成 go.mod 文件。
  2. 执行 go mod tidy` 更新整理现有的依赖,删除未使用的依赖。

go mod 相关命令

go mod download 下载 go.mod 文件中指明的所有依赖

go mod tidy 整理现有的依赖,删除未使用的依赖。

go mod graph 查看现有的依赖结构

go mod init [module] module为项目目录名,生成 go.mod 文件 (Go 1.13 中唯一一个可以生成 go.mod 文件的子命令)

go mod edit 编辑 go.mod 文件

go mod vendor 导出现有的所有依赖 (事实上 Go modules 正在淡化 Vendor 的概念)

go mod verify 校验一个模块是否被篡改过

go clean -modcache 清理所有已缓存的模块版本数据。

go mod 查看所有 go mod的使用命令。

参考资料:

https://segmentfault.com/a/1190000020522261

https://learnku.com/articles/27401

links

  • 目录
  • 上一节:
  • 下一节: