前言: 在Golang1.11之前的版本中,官方没有提供依赖和包管理工具。开发者通常会使用vendor或者glide的方式来管理依赖(也有直接使用GOPATH多环境方式),而在Golang1.11之后官方终于出了名为go modules的版本管理机制。

注意:

  • 在Golang1.11版本中需要使用export GO111MODULE=on来显式开启go module
  • 在Golang1.12之后默认开启了module

Golang Module快速入门

初始化项目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 在GOPATH之外创建一个项目目录
  mkdir cmdb-job

# 进入目录后初始化一个module
  cd cmdb-job
  go mod init cmdb-job
go: creating new go.mod: module cmdb-job

# 查看生成的mod.md
  cat go.mod
module cmdb-job

go 1.12

# 使用第三方模块编写一丢丢代码逻辑
# 一个字符串简单几种加密方式的简单工具
 cat main.go
/*================================================================
*Copyright (C) 2019 BGBiao Ltd. All rights reserved.
*
*FileName:main.go
*Author:BGBiao
*Date:2019年09月07日
*Description:
*
================================================================*/
package main
import (
    "fmt"
    "github.com/xxbandy/go-utils/crypto"
)

func main() {
	fmt.Println(crypto.Md5("hello"))
	fmt.Println(crypto.Hmac("key2", "hello"))
	fmt.Println(crypto.Sha1("hello"))
	fmt.Println(crypto.HmacSha1("key2", "hello"))
}

# 查看下项目结构和依赖
 tree -L 1 .
.
├── go.mod
├── go.sum
└── main.go

测试运行项目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 测试运行
# 第一次运行会自动检查代码里的第三方依赖包并下载和接下项目依赖的包
  go run main.go
go: finding github.com/xxbandy/go-utils/crypto latest
go: finding github.com/xxbandy/go-utils latest
go: downloading github.com/xxbandy/go-utils v0.0.0-20190506113112-d88d469a26b2
go: extracting github.com/xxbandy/go-utils v0.0.0-20190506113112-d88d469a26b2
5d41402abc4b2a76b9719d911017c592
f1b90b4efd0e5c7db52dfa0efd6521a3
aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
79806c50bab4825a371e3c4fff80bae0c731d54e

# 第二次运行
  go run main.go
5d41402abc4b2a76b9719d911017c592
f1b90b4efd0e5c7db52dfa0efd6521a3
aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
79806c50bab4825a371e3c4fff80bae0c731d54e

# 查看管理mod的依赖配置
## go.mod会记录项目所需依赖以及具体的版本号记录(go-utils这个库没有版本,所以会根据提交日期和commitid来唯一标识版本)
  cat go.mod
module cmdb-job

go 1.12

require github.com/xxbandy/go-utils v0.0.0-20190506113112-d88d469a26b2 // indirect

## go.sum文件会记录依赖树的详细信息
  cat go.sum
github.com/xxbandy/go-utils v0.0.0-20190506113112-d88d469a26b2 h1:SD6OuR8EUvCmiBDoxItcFYXIMuse45HvuSh3ho0AAeU=
github.com/xxbandy/go-utils v0.0.0-20190506113112-d88d469a26b2/go.mod h1:XhyyBI+KH4XEIbw3EhMFjZfJcV3kDK4iSAT7zZYd+Uc=

Go Module的其他功能

注意:go mod 还有一些其他比较有意思的工具,比如可以打印依赖树,比如可以查看哪些模块在哪些包引用了

go mod的其他特性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 执行go help mod可以查看到以下子命令
 go help mod
	download    download modules to local cache       # 下载模块包到本地缓存
	edit        edit go.mod from tools or scripts     # 从工具或脚本中修改go.md内容(暂时没觉得很好用)
	graph       print module requirement graph        # 打印模块依赖树
	init        initialize new module in current directory # 在当前目录初始化一个模块
	tidy        add missing and remove unused modules      # 添加或略或删除不使用的包
	vendor      make vendored copy of dependencies         # 将依赖拷贝到vendor中(会将依赖打包到当前目录下的vendor目录)
	verify      verify dependencies have expected content  # 检查依赖内容
	why         explain why packages or modules are needed # 解释为什么哪些包或者模块被需要


# 查看当前项目的依赖树(项目复杂时比较有用)
  go mod graph
cmdb-job github.com/xxbandy/go-utils@v0.0.0-20190506113112-d88d469a26b2
# 查看模块或包在哪里被依赖了
  go mod why github.com/xxbandy/go-utils/crypto
# github.com/xxbandy/go-utils/crypto
cmdb-job
github.com/xxbandy/go-utils/crypto

# 拷贝一份依赖到项目本地
# 会将项目的依赖包拷贝到项目的vendor目录中
  go mod vendor
  tree -L 3 .
.
├── go.mod
├── go.sum
├── main.go
└── vendor
    ├── github.com
       └── xxbandy
    └── modules.txt

注意:这个时候可能会有人有疑问了,既然go mod vendor将依赖拷贝一份到项目家目录的vendor目录了,那原始的依赖存在哪里?比如我们之前没有依赖管理之前,默认的依赖都是在$GOPATH/src/下面的

其实,在使用了go mod之后,项目依赖的全部依赖包都会在$GOPATH/pkg/mod/cache/download/中统一缓存下来,而项目中的go.md中会记录真正的版本信息,去缓存中引用即可。

1
2
3
4
5
6
# 查看go mod之后缓存的依赖包
➜  tree -L 3   ${GOPATH}/pkg/mod/cache/download/
${GOPATH}/pkg/mod/cache/download/
└── github.com
    └── xxbandy
        └── go-utils

一个开源项目示例

我们就以golang-gin-vue项目为例来看下go mod的一些功能吧.

golang-gin-vue是一个使用gin框架编写后端接口,结合vue来渲染前端的一个示例项目.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 克隆项目到本地
 git clone https://github.com/xxbandy/golang-gin-vue.git

  cd golang-gin-vue

# 使用go mod初始化项目
  golang-gin-vue git:(master) go mod init golang-gin-vue
go: creating new go.mod: module golang-gin-vue

# 初始化后会默认生成go.mod文件但是不会加载依赖包需要go run或者go build来自动加载依赖
  golang-gin-vue git:(master)  go build main.go
go: finding github.com/gorilla/mux v1.7.3
go: downloading github.com/gorilla/mux v1.7.3
go: extracting github.com/gorilla/mux v1.7.3

# 查看go.mod文件
  golang-gin-vue git:(master)  cat go.mod
module golang-gin-vue
go 1.12
require (
	github.com/gin-gonic/gin v1.4.0 // indirect
	github.com/gorilla/mux v1.7.3 // indirect
)

# 打印依赖树关系
## 基本上可以知道项目和库依赖了哪些库
  go mod graph
golang-gin-vue github.com/gin-gonic/gin@v1.4.0
golang-gin-vue github.com/gorilla/mux@v1.7.3
github.com/gin-gonic/gin@v1.4.0 github.com/gin-contrib/sse@v0.0.0-20190301062529-5545eab6dad3
github.com/gin-gonic/gin@v1.4.0 github.com/golang/protobuf@v1.3.1
github.com/gin-gonic/gin@v1.4.0 github.com/json-iterator/go@v1.1.6
github.com/gin-gonic/gin@v1.4.0 github.com/mattn/go-isatty@v0.0.7
github.com/gin-gonic/gin@v1.4.0 github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd
github.com/gin-gonic/gin@v1.4.0 github.com/modern-go/reflect2@v1.0.1
github.com/gin-gonic/gin@v1.4.0 github.com/stretchr/testify@v1.3.0
github.com/gin-gonic/gin@v1.4.0 github.com/ugorji/go@v1.1.4
github.com/gin-gonic/gin@v1.4.0 golang.org/x/net@v0.0.0-20190503192946-f4e77d36d62c
github.com/gin-gonic/gin@v1.4.0 gopkg.in/go-playground/assert.v1@v1.2.1
github.com/gin-gonic/gin@v1.4.0 gopkg.in/go-playground/validator.v8@v8.18.2
github.com/gin-gonic/gin@v1.4.0 gopkg.in/yaml.v2@v2.2.2
github.com/stretchr/testify@v1.3.0 github.com/davecgh/go-spew@v1.1.0
github.com/stretchr/testify@v1.3.0 github.com/pmezard/go-difflib@v1.0.0
github.com/stretchr/testify@v1.3.0 github.com/stretchr/objx@v0.1.0
github.com/mattn/go-isatty@v0.0.7 golang.org/x/sys@v0.0.0-20190222072716-a9d3bda3a223
gopkg.in/yaml.v2@v2.2.2 gopkg.in/check.v1@v0.0.0-20161208181325-20d25e280405
golang.org/x/net@v0.0.0-20190503192946-f4e77d36d62c golang.org/x/crypto@v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net@v0.0.0-20190503192946-f4e77d36d62c golang.org/x/text@v0.3.0
golang.org/x/crypto@v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/sys@v0.0.0-20190215142949-d0b11bdaac8a

# 查看gin这个模块被哪里需要(golang-gin-vue项目和gin框架本身需要)
 go mod why github.com/gin-gonic/gin
# github.com/gin-gonic/gin
golang-gin-vue
github.com/gin-gonic/gin

Tips

我们在使用Golang进行开发过程中,通常会发现各种第三方库会依赖golang.org/x之类的原生库,或者一些其他国外大厂提供的共有库,由于政策原因,我们是无法直接访问国外网站来下载依赖库的(网速也有限制),因此在开发过程中也是比较头疼的. 这个时候通常大家都会去采用科学上网,或者通过一些代理的方式来解决. 而在Golang高版本中,包含了goproxy特性,用户可以直接指定代理来下载依赖的第三方库,一方面解决了下载速度的问题,另外一方面也解决了无法访问的第三方库的下载。

注意:goproxy必须在go1.11以上版本,并且开启了go module机制后才能使用的(因为我当前的环境都是1.12,大家可以验证下小版本是否支持)

而在国内,质量比较好的几个goproxy代理大概有如下几个:

go代理使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 开启go module支持
# golang1.11 需要手动开启,golang1.12之后默认会开启
$ export GO111MODULE=on

# 设置代理(上述三个代理地址都可以)
$ export GOPROXY=https://goproxy.cn

# 下载第三方库

$ go get -v golang.org/x/net/websocket
Fetching https://goproxy.cn/golang.org/x/net/websocket/@v/list
Fetching https://goproxy.cn/golang.org/x/net/@v/list
go: finding golang.org/x/net latest
Fetching https://goproxy.cn/golang.org/x/net/@latest
Fetching https://goproxy.cn/golang.org/x/net/@v/v0.0.0-20190827160401-ba9fcec4b297.mod
golang.org/x/net/websocket

自从使用了goproxy之后,妈妈再也不用担心无法下载第三方库拉,爽到不行.

知识星球

公众号