Go Module
最早的时候,Go语言所依赖的所有的第三方库都放在 GOPATH 这个目录下面,这就导致了同一个库只能保存一个版本的代码。如果不同的项目依赖同一个第三方的库的不同版本,应该怎么解决?
go module 是Go语言从 1.11 版本之后官方推出的版本管理工具,并且从 Go1.13 版本开始,go module 成为了Go语言默认的依赖管理工具。
Modules 官方定义为:
Modules 是相关 Go 包的集合,是源代码交换和版本控制的单元。Go语言命令直接支持使用 Modules,包括记录和解析对其他模块的依赖性,Modules 替换旧的基于 GOPATH 的方法,来指定使用哪些源文件。
1. 如何开启Modules?
- 首先需要把 golang 升级到 1.11 版本以上。
- 设置
GO111MODULE
。
1.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 的命令为:
1 | set GO111MODULE=on |
MacOS 或者 Linux 下开启 GO111MODULE 的命令为:
1 | export GO111MODULE=on |
在开启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 | 解释为什么需要依赖 |
1.2. GOPROXY
proxy 顾名思义就是代理服务器的意思。大家都知道,国内的网络有防火墙的存在,这导致有些Go语言的第三方包我们无法直接通过go get
命令获取。GOPROXY
是Go语言官方提供的一种通过中间代理商来为用户提供包下载服务的方式。要使用GOPROXY
只需要设置环境变量GOPROXY
即可。
目前公开的代理服务器的地址有:
- goproxy.io;
- goproxy.cn:(推荐)由国内的七牛云提供。
Windows 下设置 GOPROXY 的命令为:
1 | go env -w GOPROXY=https://goproxy.cn,direct |
MacOS 或 Linux 下设置 GOPROXY 的命令为:
1 | export GOPROXY=https://goproxy.cn |
Go语言在 1.13 版本之后 GOPROXY 默认值为:https://proxy.golang.org
在国内可能会存在下载慢或者无法访问的情况,所以十分建议大家将GOPROXY
设置为国内的goproxy.cn
。
1.3. 使用go get命令下载指定版本的依赖包
执行go get 命令,在下载依赖包的同时还可以指定依赖包的版本。
- 运行go get -u命令会将项目中的包升级到最新的次要版本或者修订版本;
- 运行go get -u=patch命令会将项目中的包升级到最新的修订版本;
- 运行go get [包名]@[版本号]命令会下载对应包的指定版本或者将对应包升级到指定的版本。
❗️提示:
go get [包名]@[版本号]
命令中版本号可以是x.y.z
的形式,例如:1
go get foo@v1.2.3
- 也可以是 git 上的分支或 tag,例如:
1
go get foo@master
- 还可以是 git 提交时的哈希值,例如:
1
go get foo@e3702bed2
2. 如何在项目中使用Modules?
2.1. 示例1
创建一个新项目:
初始化生成go.mod
在GOPATH
目录之外新建一个目录,并使用go mod init
初始化生成go.mod
文件。1
go mod init 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 文件如下所示:
1
2
3module hello
go 1.18添加依赖。
新建一个main.go文件,写入以下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14package 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"))
}执行go run main.go后会显示:
1
2main.go:4:2: no required module provides package github.com/labstack/echo; to add it:
go get github.com/labstack/echo提示需要下载的依赖为
github.com/labstack/echo
。根据提示下载该依赖:
1
go get github.com/labstack/echo
可以看到它开始下载所需依赖。具体执行情况如下图所示:
现在查看 go.mod 内容:
go module安装 package 的原则是先拉取最新的 release tag,若无 tag 则拉取最新的 commit,详见 Modules 官方介绍。
go 会自动生成一个
go.sum
文件来记录 dependency tree:再次执行脚本go run main.go发现跳过了检查并安装依赖的步骤:
- 可以使用命令
go list -m -u all
来检查可以升级的 package; - 使用
go get -u need-upgrade-package
升级后会将新的依赖版本更新到go.mod
; - 也可以使用
go get -u
升级所有依赖。
- 可以使用命令
2.2. 示例2
改造现有项目。
项目目录结构为:
1 | |--- main.go |
main.go 源码为:
1 | package main |
api/apis.go 源码为:
1 | package api |
使用
go mod init hello
初始化go.mod
1
go mod init hello
显示使用go mod tidy来添加requirements和sums:
1
2
3go: creating new go.mod: module hello
go: to add module requirements and sums:
go mod tidy运行
go mod tidy
运行结果如下图所示:
可以看到,出现了这样的提示:
1
2hello imports
./api: "./api" is relative, but relative import paths are not supported in module mode说明相对路径的引用go mod不支持。
更新旧的 package import 方式
将main.go改写成:1
2
3
4
5
6
7
8
9
10
11package main
import (
api "hello/api" // 这里使用的是相对路径
"github.com/labstack/echo"
)
func main() {
e := echo.New()
e.GET("/", api.HelloWorld)
e.Logger.Fatal(e.Start(":1323"))
}到这里就和新创建一个项目没什么区别了:
3. 使用 replace 替换无法直接获取的 package
由于某些已知的原因,并不是所有的 package 都能成功下载,比如:golang.org
下的包。
modules 可以通过在 go.mod
文件中使用 replace 指令替换成 github 上对应的库,比如:
1 | replace ( |
或者:
1 | replace golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a |