Appearance
Go Modules
1. 概述
最早的时候,Go 语言所依赖的所有的第三方库都放在 GOPATH 这个目录下面,这就导致了同一个库只能保存一个版本的代码。如果不同的项目依赖同一个第三方的库的不同版本,应该怎么解决?
go module 是 Go 语言从 1.11 版本之后官方推出的版本管理工具,并且从 Go1.13 版本开始,go module 成为了 Go 语言默认的依赖管理工具。
Modules 官方定义为:
Modules 是相关 Go 包的集合,是源代码交换和版本控制的单元。Go 语言命令直接支持使用 Modules,包括记录和解析对其他模块的依赖性,Modules 替换旧的基于 GOPATH 的方法,来指定使用哪些源文件。
2. 如何使用 Modules?
首先需要把 golang 升级到 1.11 版本以上
设置 GO111MODULE
2.1. GO111MODULE
在 Go 语言 1.12 版本之前,要启用 go module 工具首先要设置环境变量 GO111MODULE,不过在 Go 语言 1.13 及以后的版本则不再需要设置环境变量。通过 GO111MODULE 可以开启或关闭 go module 工具。
GO111MODULE=off禁用 go module,编译时会从 GOPATH 和 vendor 文件夹中查找包;GO111MODULE=on启用 go module,编译时会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖;GO111MODULE=auto(默认值),当项目在 GOPATH/src 目录之外,并且项目根目录有 go.mod 文件时,开启 go module;
Windows 下开启 GO111MODULE 的命令为:
Text
set GO111MODULE=on 或者 set GO111MODULE=autoMacOS 或者 Linux 下开启 GO111MODULE 的命令为:
Text
export GO111MODULE=on 或者 export GO111MODULE=auto在开启 GO111MODULE 之后就可以使用 go module 工具了,也就是说在以后的开发中就没有必要在 GOPATH 中创建项目了,并且还能够很好的管理项目依赖的第三方包信息。
常用的 go mod 命令如下表所示:
| 命令 | 作用 |
|---|---|
| go mod download | 下载依赖包到本地(默认为 GOPATH/pkg/mod 目录) |
| go mod edit | 编辑 go.mod 文件 |
| go mod graph | 打印模块依赖图 |
| go mod init | 初始化当前文件夹,创建 go.mod 文件 |
| go mod tidy | 增加缺少的包,删除无用的包 |
| go mod vendor | 将依赖复制到 vendor 目录下 |
| go mod verify | 校验依赖 |
| go mod why | 解释为什么需要依赖 |
2.2. GOPROXY
proxy 顾名思义就是代理服务器的意思。大家都知道,国内的网络有防火墙的存在,这导致有些 Go 语言的第三方包我们无法直接通过 go get 命令获取。GOPROXY 是 Go 语言官方提供的一种通过中间代理商来为用户提供包下载服务的方式。要使用 GOPROXY 只需要设置环境变量 GOPROXY 即可。
目前公开的代理服务器的地址有:
goproxy.io;goproxy.cn:(推荐)由国内的七牛云提供;
Windows 下设置 GOPROXY 的命令为:
PowerShell
go env -w GOPROXY=https://goproxy.cn,directMacOS 或 Linux 下设置 GOPROXY 的命令为:
Bash
$ export GOPROXY=https://goproxy.cnGo 语言在 1.13 版本之后 GOPROXY 默认值为 https://proxy.golang.org,在国内可能会存在下载慢或者无法访问的情况,所以十分建议大家将 GOPROXY 设置为国内的 goproxy.cn。
附:go set proxy。
2.3. go get
使用 go get 命令下载指定版本的依赖包。
执行 go get 命令,在下载依赖包的同时还可以指定依赖包的版本。
- 运行
go get -u命令会将项目中的包升级到最新的次要版本或者修订版本; - 运行
go get -u=patch命令会将项目中的包升级到最新的修订版本; - 运行
go get [包名]@[版本号]命令会下载对应包的指定版本或者将对应包升级到指定的版本。
Note
go get [包名]@[版本号]命令中版本号可以是x.y.z的形式,例如go get foo@v1.2.3,也可以是 git 上的分支或 tag,例如go get foo@master,还可以是 git 提交时的哈希值,例如go get foo@e3702bed2。
3. 如何在项目中使用
3.1. 示例 1
创建一个新项目:
在 GOPATH 目录之外新建一个目录,并使用
go mod init初始化生成go.mod文件。go mod init hello go: creating new go.mod: module hello
go.mod文件一旦创建后,它的内容将会被 go toolchain 全面掌控,go toolchain 会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。go.mod提供了module、require、replace和exclude四个命令:module语句指定包的名字(路径);require语句指定的依赖项模块;replace语句可以替换依赖项模块;exclude语句可以忽略依赖项模块;
初始化生成的 go.mod 文件如下所示:
Textmodule hello go 1.131
2
3添加依赖。
新建一个 main.go 文件,写入以下代码:
Gopackage main import ( "net/http" "github.com/labstack/echo" ) func main() { e := echo.New() e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) e.Logger.Fatal(e.Start(":1323")) }1
2
3
4
5
6
7
8
9
10
11
12执行
go run main.go运行代码会发现go mod会自动查找依赖自动下载:Textgo run main.go go: finding github.com/labstack/echo v3.3.10+incompatible go: downloading github.com/labstack/echo v3.3.10+incompatible go: extracting github.com/labstack/echo v3.3.10+incompatible go: finding github.com/labstack/gommon v0.3.0 ...... go: finding golang.org/x/text v0.3.0 ____ __ / __/___/ / ___ / _// __/ _ \/ _ \ /___/\__/_//_/\___/ v3.3.10-dev High performance, minimalist Go web framework https://echo.labstack.com ____________________________________O/_______ O\ ⇨ http server started on [::]:13231
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17现在查看
go.mod内容:Textmodule hello go 1.13 require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.3.0 // indirect golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect )1
2
3
4
5
6
7
8
9
go module 安装 package 的原则是先拉取最新的 release tag,若无 tag 则拉取最新的 commit,详见 Modules 官方介绍。
go 会自动生成一个 go.sum 文件来记录 dependency tree:
Text
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
... 省略很多行1
2
3
4
5
6
7
2
3
4
5
6
7
再次执行脚本 go run main.go 发现跳过了检查并安装依赖的步骤。
可以使用命令 go list -m -u all 来检查可以升级的 package,使用 go get -u need-upgrade-package 升级后会将新的依赖版本更新到 go.mod,也可以使用 go get -u 升级所有依赖。
3.2. 示例 2
改造现有项目。
项目目录结构为:
Text
├─ main.go
│
└─ api
└─ apis.go1
2
3
4
2
3
4
main.go 源码为:
Go
package main
import (
api "./api" // 这里使用的是相对路径
"github.com/labstack/echo"
)
func main() {
e := echo.New()
e.GET("/", api.HelloWorld)
e.Logger.Fatal(e.Start(":1323"))
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
api/apis.go 源码为:
Go
package api
import (
"net/http"
"github.com/labstack/echo"
)
func HelloWorld(c echo.Context) error {
return c.JSON(http.StatusOK, "hello world")
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
使用
go mod init初始化go.mod。Bash$ go mod init hello go: creating new go.mod: module hello1
2运行
go run main.go。Bash$ go run main.go go: finding golang.org/x/crypto latest build _/D_/code/src/api: cannot find module for path _/D_/code/src/api1
2
3首先还是会查找并下载安装依赖,然后运行脚本
main.go,这里会抛出一个错误:Textbuild _/D_/code/src/api: cannot find module for path _/D_/code/src/api但是
go.mod已经更新:Textmodule hello go 1.13 require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.3.0 // indirect golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect )1
2
3
4
5
6
7
8
9那为什么会抛出这个错误呢?
这是因为
main.go中使用 internal package 的方法跟以前已经不同了,由于go.mod会扫描同工作目录下所有 package 并且变更引入方法,必须将 hello 当成路径的前缀,也就是需要写成import hello/api,以往GOPATH/dep模式允许的import ./api已经失效。更新旧的 package import 方式。
所以
main.go需要改写成:Gopackage main import ( api "hello/api" // 这里使用的是相对路径 "github.com/labstack/echo" ) func main() { e := echo.New() e.GET("/", api.HelloWorld) e.Logger.Fatal(e.Start(":1323")) }1
2
3
4
5
6
7
8
9
10Note
在 Go 语言 1.11 版本下使用
go mod时可能会遇到go build github.com/valyala/fasttemplate: module requires go 1.12这种错误,遇到类似这种需要升级到 1.12 的问题,直接升级到 Go 语言 1.12 版本以上就好了。到这里就和新创建一个项目没什么区别了。
4. 使用 replace 替换无法直接获取的 package
由于某些已知的原因,并不是所有的 package 都能成功下载,比如:golang.org 下的包。
modules 可以通过在 go.mod 文件中使用 replace 指令替换成 github 上对应的库,比如:
Text
replace (
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)或者
Text
replace golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a