Go Modules笔记
介绍
module是go官方推出的模块管理工具,每个模块就相当于java中的一个包。
每个 module 中都包含一个 go.mod 文件,反之:没有go.mod文件,就不是一个module。
go.mod文件定义了该模块自身的 module path,同时也记录了该模块的依赖包;看下面的一个简单的 go.mod 的例码:
$ cat go.mod
module example.com/hello # 自己 module 的 “module path”
go 1.12
require rsc.io/quote v1.5.2 # 当前 module 依赖的 “module path” 和 版本号
$
除了 go.mod
之外,go 命令行工具还维护了一个 go.sum
文件,它包含了 指定版本模块内容的哈希值,用于校验。
简单使用
首先需要知道的两点:
- 如果你使用的是1.11和1.12版本的GO,当你工作目录是在
$GOPATH/src
里面的时候,go modules 功能不会被启用;依旧按照旧版本的规则,从GOPATH中寻找你导入的包,即使你目录中有 go.mod 文件,也不会启用。 - 如果你使用的1.13版本的GO时,无论你的项目路径在哪里,只要项目中存在go.mod文件,都会启用 go moduls 功能。
创建一个新 module
你可以在一个新目录下编写一些简单的go代码,或者用自己之前写过的go项目,在最上层目录中执行初始化module的命令:
# go mod init ${自定义的模块路径}
go mod init example.com/helloworld
效果
你会发现,当前目录下生成了一个 go.mod 文件,里边就会有自己定义的 module 路径,也就是上边命令中的 example.com/helloworld
。这样你就创建好了一个新 module,这个文件夹里边的所有包,包括子文件中的子包共同组成了这个 module。
导入其他 module
当你用的是以前写的项目时,文件中导的包将不会从GOPATH中读取,而是会从远程仓库中拉取,并且缓存到$GOPATH/pkg/mod
中。
代码中 import 了一个包,但 go.mod
文件里面又没有指定这个包的时候,尝试着去编译代码时,系统会自动寻找包含这个代码包的模块的最新稳定版本。
这个时候可能会报错,其中出现次数最多的几个原因是:
- 使用GOPATH导包的路径和 module 不一样,去远程仓库拉取时,找不到想要的包;
- 因为拉取的包是最新稳定版,和当前使用的版本不兼容,导致代码运行不起来;
如何让他人使用自己的module
创建仓库
在GitHub上创建自己的仓库,假设我的仓库名为 gomodule-test。
编写一段简单的代码
package foo
import "fmt"
func Foo(name string) {
fmt.Println("foo print -> "+name)
}
初始化自己的module
在自己仓库的根目录下执行命令:
# github/shalom/gomodule-test 是自定义的 module path
go mod init github/shalom/gomodule-test
注意:这个 module path 要和GitHub上的路径一样
他人如何使用
你推送到GitHub上后,他人就可以像下边代码一样使用你的module,例码:
package main
import "github/shalom/gomodule-test/foo"
func main() {
foo.Foo("shalom")
}
执行编译命令 go build
后,go会根据 github/shalom/gomodule-test
(module path) 自动拉取相关代码,并缓存到 $GOPATH/pkg/mod
里边
在IDEA中设置go module
当自己在使用 go module 时,为了让IDEA不飘红,可以这么设置:Setting -> 搜索 module -> 设置下图两个地方:
- 一个是在IDEA中开启 module 功能
- 另一个是设置代理
仓库,模块和包之间的关系
- 仓库包含一个或多个 Go 模块
- 每个模块包含一个或多个 Go 包
- 每个包由一个目录中的一个或多个 Go 源文件组成
go modules相关的环境变量
GO111MODULE
此环境变量用来:开启和关闭 modules功能。
它有三个可选值:off、on、auto,默认值是 auto。
export GO111MODULE=off
- 无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包。
export GO111MODULE=on
-
强制模块支持;
- 1.11和1.12版本,只要项目目录中存在go.mod文件,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖;
- 1.13版,即使目录中或者上级目录中不存在 go.mod 文件,也同样会忽略 GOPATH 和 vendor 文件夹,使用module功能拉取代码中导入的包。
export GO111MODULE=auto
- 1.11和1.12版本,在 $GOPATH/src 外面且根目录有 go.mod 文件时,开启模块支持;
- 1.13版,是否启用module功能取决于当前目录,若当前目录存在 go.mod 文件,或者任意级的父目录中存在 go.mod 文件,无论是否在GOPATH下,都将使用 go module。
GOPROXY
此环境变量用来:指定 Go 模块代理。这个代理可以理解为一个 云端的包管理平台,用于存储并提供下载包。
Go 1.13 将 GOPROXY
默认为:https://proxy.golang.org;这个网址是中国大陆无法访问的。
国内代理介绍
- goproxy.cn:由七牛云运行,支持代理 sum.golang.org,经过 CDN 加速,高可用,可应用进公司复杂的开发环境中,亦可用作上游代理;
-
mirrors.aliyun.com/goproxy:由阿里云运行,不支持 sumdb 代理,经过 CDN 加速,高可用,可应用进公司复杂的开发环境中,亦可用作上游代理;
- 此代理在拉取
github.com/kubernetes/kubernetes
库时,会出现 404
- 此代理在拉取
- goproxy.io:由个人运行,支持代理 sum.golang.org。
有人对goproxy.cn和goproxy.io进行了一组测试:在相同的网络环境下,分别拉取超大型库 github.com/kubernetes/kubernetes。测试结果为:
- goproxy.cn 耗时 19.009s;
- goproxy.io 耗时 70.03s;
所以综合对比下来,使用 goproxy.cn 最佳。
如何设置GOPROXY
go env -w GOPROXY=https://goproxy.cn,direct
-
-w
用来修改环境变量的默认值,具体细节可以使用go help env
命令查看; -
,direct
是1.13版本的新功能,可以在一定程度上解决私有库的问题,细节如下:- 首先 go 会在 GOPROXY 设置的代理平台上抓取目标模块;
- 如果是私库或者其他原因导致了 404 错误,那么就回退到
direct
;direct
指的是 module 的源头,比如你导入的模块是github.com/test
,那么源头就是 GitHub,由GitHub来判断你是否有权限访问目标库; - 而当你在GitHub等类似的代码托管网站抓取的时候,若你无权访问,他就会告诉你此包不存在,若有权访问,则可以正常拉取;
配套变量GONOPROXY
上边加,direct
的方式并不十分完美,因为每次拉取私库时,都要经过代理。通过设置GONOPROXY
,可以直接无脑绕过代理,。
GOSUMDB
GOPRIVATE
私库处理
如何才能够畅通的解决私库问题,
go.mod文件
go.mod 文件有四个指令:module
,require
,replace
,exclude
。
-
module
指令在 go.mod 中声明模块标识,该指令声明模块的路径。 -
require
标明当前module的依赖。 -
// indirect
间接依赖
go.sum文件
TODO ...
go mod 子命令
download download modules to local cache (下载依赖的module到本地cache))
edit edit go.mod from tools or scripts (编辑go.mod文件)
graph print module requirement graph (打印模块依赖图))
init $modulePath initialize new module in current directory (在当前文件夹下初始化一个新的module, 创建go.mod文件))
tidy add missing and remove unused modules (增加丢失的module,去掉未用的module)
vendor make vendored copy of dependencies (将依赖复制到vendor下)
verify verify dependencies have expected content (校验依赖)
why explain why packages or modules are needed (解释为什么需要依赖)
辅助命令
-
go list -m all
命令会把当前的模块所有的依赖项都列出来(包括间接依赖);
go module版本问题
版本号介绍
模块的版本包括三个部分:主版本号(major)、次版本号(minor)、修订号(patch)。举个例子:对于版本 v0.1.2
,主版本号是 0,次版本号是 1,修订号是 2。
作为Author(作者)
给module打tag
当我们修改完代码,并且已经commit了后,在push之前,给module打上tag
$ git tag v1.0.0
$ git push --tag origin master
如果不打 tag,go会在 consumer(module的使用者) 的go.mod中使用伪版本号,比如:
github.com/shalom/testmodule/v2 v2.0.0-20190603050009-28a5b8da279e
module的升级
完善代码后,要给原本是 v1.0.0 的module 升级为 v2.0.0
作为Customer(使用者)
若新版本 module 的path发生的改变,则需要手动修改代码中的导包路径
可使用 go get rsc.io/sampler@v1.3.1
命令进行module的版本更新
运行 go get -u=patch
将会升级到最新的修订版本(比如说,将会升级到 1.0.1 版本,但不会升级到 1.1.0 版本)
有疑问加站长微信联系(非本文作者)