Go Modules是Go 1.11引入的,为了解决Go项目的依赖问题。从go 1.16开始,go modules开始成为Go默认的包依赖管理工具。

go moudle是一组go package的集合

一个Go Module实际上是由一组Go的Package的集合。 构建Go Module的过程需要先确定依赖的其他Go Module的版本、编译包、将编译的目标文件链接到一起。

一个Go项目就是使用go mod创建的一个Go Module,由多个Go package组成,这个项目被保存在一个代码库中(如git)。 也就是说这个代码库中有多个go的package,如https://github.com/grpc/grpc-go这个项目。 每个Go Module根目录中会有一个go.mod文件描述module的元数据信息。

看一下grpc-go这个项目的目录结构:

 1./grpc-go
 2├── Documentation
 3├── admin
 4├── attributes
 5├── authz
 6├── backoff
 7├── balancer
 8├── benchmark
 9├── binarylog
10├── channelz
11├── cmd
12├── codes
13├── connectivity
14├── credentials
15├── encoding
16├── examples
17├── grpclog
18├── health
19├── internal
20├── interop
21├── keepalive
22├── metadata
23├── peer
24├── profiling
25├── reflection
26├── resolver
27├── security
28├── serviceconfig
29├── stats
30├── status
31├── stress
32├── tap
33├── test
34├── testdata
35├── xds
36...
37... (省略了根目录里的一些go源码和其他的一些文件)
38...
39├── server.go
40├── go.mod
41├── go.sum
42└── xds

可以看到grpc-go这个go module就是由一组go pacakge组成,在根目录里有一个go.mod文件,内容如下:

 1module google.golang.org/grpc
 2
 3go 1.14
 4
 5require (
 6	github.com/cespare/xxhash/v2 v2.1.1
 7	github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4
 8	github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1
 9	github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021
10	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
11	github.com/golang/protobuf v1.4.3
12	github.com/google/go-cmp v0.5.0
13	github.com/google/uuid v1.1.2
14	golang.org/x/net v0.0.0-20200822124328-c89045814202
15	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
16	golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
17	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
18	google.golang.org/protobuf v1.25.0
19)
  • go.mod文件的第1行声明了go moudle的路径(path)。grpc-go这个module的path是google.golang.org/grpc。 moudle path有点其他语言中的命名空间的意思,当需要导入go module中的某个包时,需要以module-path/包子目录名称,例如: import "google.golang.org/grpc/codes"导入了google.golang.org/grpc这个go module的codes包。
  • go.mod文件第3行的go 1.14用于指明Go的版本,用来说明go.mod文件是使用哪个Go版本中go module语义编写的。因为go mod被直接集成为go的子命令。
  • require块中的内容用来配置go module依赖了哪些外部的go module。

go module基于语义化版本

go moudle基于语义化版本,采用v<major>.<minor>.<patch>的格式,例如grpc-go依赖的github.com/google/uuid这个go moudle的版本是v1.1.2。 语义版号的递增规则如下:

  • major(主版本号): 当做了不兼容的API修改时,一般是重大架构、技术、功能升级,API已经不兼容原来的版本
  • minor(次版本): 当做了向下兼容的功能性新新增,一般是正常的版本、功能迭代,要求API向后兼容
  • patch(修订版本号):当做了向下兼容的问题修正,要求API向后兼容

在语义化版本的规范下,使用github.com/google/uuid这个外部依赖的话,grpc-go选择任何v1.x.y都是兼容现在的代码的,例如后续的v1.1.3, v1.2.0。 如果有一天github.com/google/uuid发布了v2.0.0的版本,grpc-go直接修改了go.mod升级了依赖的版本到v2.0.0,如果按照前面的go module依赖导入规则,就会出现编译错误,因为主版本号升级后不承诺API的兼容性。 针对这个问题Go Module给的解决方案是,从主版本号的2开始将主版本号加入到go moudle的path中,具体规则如下:

语义化版本 module path 导入go moudle中的包
v0.x.y github.com/google/uuid import “github.com/google/uuid”
v1.x.y github.com/google/uuid import “github.com/google/uuid”
v2.x.y github.com/google/uuid/v2 import “github.com/google/uuid/v2”
v3.x.y github.com/google/uuid/v3 import “github.com/google/uuid/v3”

这样如果将项目依赖的外部go moudle的主版本号升级时,就需要切换moudle path和代码中导入package路径,同时对使用的不兼容的API做出修改调整。

关于传递依赖如何选择,例如我们正在开发的A moudle同时依赖了B module和C moudle,而B依赖v1.1.0的D,C依赖v1.2.0的D,D的最新版本是v1.3.0。 使用go mod时,项目A通过传递依赖使用的C的版本将会是v1.2.0。因为按照语义化版本,主版本号相同的话,次版本号越大的其稳定性和安全性应该是更好的,由于B和C传递的版本中选择了次版本号更大的版本。

命令

go modules直接作为子命令集成在go中:

 1go mod
 2Go mod provides access to operations on modules.
 3
 4Note that support for modules is built into all the go commands,
 5not just 'go mod'. For example, day-to-day adding, removing, upgrading,
 6and downgrading of dependencies should be done using 'go get'.
 7See 'go help modules' for an overview of module functionality.
 8
 9Usage:
10
11	go mod <command> [arguments]
12
13The commands are:
14
15	download    download modules to local cache
16	edit        edit go.mod from tools or scripts
17	graph       print module requirement graph
18	init        initialize new module in current directory
19	tidy        add missing and remove unused modules
20	vendor      make vendored copy of dependencies
21	verify      verify dependencies have expected content
22	why         explain why packages or modules are needed
23
24Use "go help mod <command>" for more information about a command.

参考