Go Module

自从 Go 官方从去年推出 1.11 之后,增加新的依赖管理模块并且更加易于管理项目中所需要的模块。模块是存储在文件树中的 Go 包的集合,其根目录中包含 go.mod 文件。 go.mod 文件定义了模块的模块路径,它也是用于根目录的导入路径,以及它的依赖性要求。每个依赖性要求都被写为模块路径和特定语义版本。

从 Go 1.11 开始,Go 允许在 $GOPATH/src 外的任何目录下使用 go.mod 创建项目。在 $GOPATH/src 中,为了兼容性,Go 命令仍然在旧的 GOPATH 模式下运行。从 Go 1.13 开始,模块模式将成为默认模式。

启用Go Modules

go env -w GO111MODULE=on # 111代表版本1.11
go env -w GOPROXY=https://goproxy.io # 配置代理
# go env -w GOPROXY=https://goproxy.cn

使用 go env 可以查询所有的环境变量,用于确认当前的配置

创建一个新模块

# 创建项目目录
$ mkdir backend && cd backend

# 初始化模块,将会生成go.mod文件
$ go mod init backend
go: creating new go.mod: module backend

$ ll
total 8
-rw-r--r--  1 yangsx  staff    24B  9 17 13:32 go.mod

$ cat go.mod
module backend # init命令指定的项目名称

go 1.17 # 当前项目版本号

添加依赖项

# 安装依赖
$ go get -u github.com/gin-gonic/gin
go: downloading golang.org/x/crypto v0.0.0-20211202192323-5770296d904e
go: downloading golang.org/x/sys v0.0.0-20211205182925-97ca703d548d
go get: added github.com/gin-contrib/sse v0.1.0
go get: added github.com/gin-gonic/gin v1.7.7
go get: added github.com/go-playground/locales v0.14.0
go get: added github.com/go-playground/universal-translator v0.18.0
go get: added github.com/go-playground/validator/v10 v10.9.0
go get: added github.com/golang/protobuf v1.5.2
go get: added github.com/json-iterator/go v1.1.12
go get: added github.com/leodido/go-urn v1.2.1
go get: added github.com/mattn/go-isatty v0.0.14
go get: added github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
go get: added github.com/modern-go/reflect2 v1.0.2
go get: added github.com/ugorji/go/codec v1.2.6
go get: added golang.org/x/crypto v0.0.0-20211202192323-5770296d904e
go get: added golang.org/x/sys v0.0.0-20211205182925-97ca703d548d
go get: added golang.org/x/text v0.3.7
go get: added google.golang.org/protobuf v1.27.1
go get: added gopkg.in/yaml.v2 v2.4.0

# 查看go.mod
$ cat go.mod
module backend

go 1.17

require (
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.7.7 // indirect
	github.com/go-playground/locales v0.14.0 // indirect
	github.com/go-playground/universal-translator v0.18.0 // indirect
	github.com/go-playground/validator/v10 v10.9.0 // indirect
	github.com/golang/protobuf v1.5.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/leodido/go-urn v1.2.1 // indirect
	github.com/mattn/go-isatty v0.0.14 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/ugorji/go/codec v1.2.6 // indirect
	golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect
	golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
	golang.org/x/text v0.3.7 // indirect
	google.golang.org/protobuf v1.27.1 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
)

# 导入gin并使用他启动一个web服务器
$ echo 'package main

import "github.com/gin-gonic/gin"

func main() {
        r := gin.Default()
        r.GET("/ping", func(c *gin.Context) {
                c.JSON(200, gin.H{
                        "message": "pong",
                })
        })
        r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}' > main.go

$ ll
total 40
-rw-r--r--  1 yangsx  staff   933B 12  7 09:07 go.mod
-rw-r--r--  1 yangsx  staff   9.0K 12  7 09:07 go.sum
-rw-r--r--  1 yangsx  staff   252B 12  7 09:09 main.go

# 执行main函数,启动服务器
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

indirect注释

在执行命令go mod tidy时,Go module 会自动整理go.mod 文件,如果有必要会在部分依赖包的后面增加// indirect注释。一般而言,被添加注释的包肯定是间接依赖的包,而没有添加// indirect注释的包则是直接依赖的包,即明确的出现在某个import语句中。

查询该mod的依赖项

# 查询所有依赖项,可以看到依赖坐标以及版本
# 这里的大部分依赖都是gin依赖的
$ go list -m all
backend
github.com/creack/pty v1.1.9
github.com/davecgh/go-spew v1.1.1
github.com/gin-contrib/sse v0.1.0
github.com/gin-gonic/gin v1.7.7
github.com/go-playground/assert/v2 v2.0.1
github.com/go-playground/locales v0.14.0
github.com/go-playground/universal-translator v0.18.0
github.com/go-playground/validator/v10 v10.9.0
github.com/golang/protobuf v1.5.2
github.com/google/go-cmp v0.5.5
github.com/google/gofuzz v1.0.0
github.com/json-iterator/go v1.1.12
github.com/kr/pretty v0.3.0
github.com/kr/pty v1.1.1
github.com/kr/text v0.2.0
github.com/leodido/go-urn v1.2.1
github.com/mattn/go-isatty v0.0.14
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
github.com/modern-go/reflect2 v1.0.2
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e
github.com/pmezard/go-difflib v1.0.0
github.com/rogpeppe/go-internal v1.8.0
github.com/stretchr/objx v0.1.0
github.com/stretchr/testify v1.7.0
github.com/ugorji/go v1.2.6
github.com/ugorji/go/codec v1.2.6
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
golang.org/x/text v0.3.7
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
google.golang.org/protobuf v1.27.1
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/errgo.v2 v2.1.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b

升级/回退依赖

# 查询gin的所有版本
$ 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.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

# 升级/回退到某个版本
$ go get github.com/gin-gonic/gin@v1.1.4
go: downloading github.com/gin-gonic/gin v1.1.4
go: downloading golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3
go: downloading gopkg.in/go-playground/validator.v8 v8.18.2
go: downloading github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739
go get: downgraded github.com/gin-gonic/gin v1.7.4 => v1.1.4
go get: added github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739
go get: added gopkg.in/go-playground/validator.v8 v8.18.2

# 查看 go.mod 可以发现gin已经被回退了
$ cat go.mod
module backend

go 1.16

require (
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.1.4 // indirect ************** 回退到了1.1.4 *****************
	github.com/go-playground/validator/v10 v10.4.1 // indirect
	github.com/golang/protobuf v1.3.3 // indirect
	github.com/json-iterator/go v1.1.9 // indirect
	github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739 // indirect
	github.com/mattn/go-isatty v0.0.12 // indirect
	github.com/ugorji/go v1.1.7 // indirect
	gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
	gopkg.in/yaml.v2 v2.2.8 // indirect
)

整理依赖项

如果项目中的一些依赖没有下载,或者有一些代码没有使用到的依赖,可以使用如下方式对依赖整理:

$ go mod tidy
go: finding module for package gopkg.in/go-playground/assert.v1
go: downloading gopkg.in/go-playground/assert.v1 v1.2.1
go: found gopkg.in/go-playground/assert.v1 in gopkg.in/go-playground/assert.v1 v1.2.1

下载依赖项

下载依赖到本地的cache路径,cache路径为GOMODCACHE:

$ go env | grep GOMODCACHE
GOMODCACHE="/Users/yangsx/go/pkg/mod"

排除/替换依赖

module my/thing // 定义模块路径
go 1.12 // 设置预期的语言版本
require other/thing v1.0.2 
require new/thing/v2 v2.3.4 // indirect
require( //  要求给定版本或更高版本的特定模块
  new/thing v2.3.4
  old/thing v0.0.0-20190603091049-60506f45cf65

exclude old/thing v1.2.3 // 排除特定版本模块的使用,不允许的模块版本被视为不可用,并且查询无法返回
replace bad/thing v1.4.5 => good/thing v1.4.5  // 使用不同的模块版本替换原有模块版本

或者使用go mod edit命令进行操作。

版本管理文件:go.sum

主要为了防止包被恶意篡改,比如有一天gin1.14.1的版本在不知情的情况下被恶意修改了,这时,虽然路径与版本号没变,但是预期hash变化了,就会被go mod verify发现。

$ cat go.sum
# 由  模块导入路径 模块版本 预期hash 三个部分组成
# 在每次缺少模块时,如果缓存中不存在,则需要下载并计算其哈希添加到 go.sum 中;
# 如果缓存中存在,则需要匹配 go.sum 中的已有条目。
# 这样,构建软件的用户就可以使用哈希验证其构建是否跟你的构建相同(go mod verify),
# 而无论他们怎样获取依赖项,都可以得到相同的版本。同时也保证了项目依赖不会发生预料之外的恶意修改和其他问题。
# 这也是为什么要将 go.sum 文件加入版本管理(Git)的原因。

再加上 Go Modules 选择的是最小版本选择策略(默认使用构建中涉及的每个模块的最旧允许版本,使得新版本的发布对构建没有影响)就可以实现可重现的构建(在重复构建时产生相同的结果)。
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-gonic/gin v1.1.4 h1:XLaCFbU39SSGRQrEeP7Z7mM3lvRqC4vE5tEaVdLDdSE=
github.com/gin-gonic/gin v1.1.4/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739 h1:ykXz+pRRTibcSjG1yRhpdSHInF8yZY/mfn+Rz2Nd1rE=
github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739/go.mod h1:zUx1mhth20V3VKgL5jbd1BSQcW4Fy6Qs4PZvQwRFwzM=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=


$ go mod verify
all modules verified

最后更新于