https://learnku.com/articles/27401
模块是存储在文件树中的 Go 包的集合,其根目录中包含 go.mod 文件。 go.mod 文件定义了模块的模块路径,它也是用于根目录的导入路径,以及它的依赖性要求。每个依赖性要求都被写为模块路径和特定语义版本。
从 Go 1.11 开始,Go 允许在 $GOPATH/src 外的任何目录下使用 go.mod 创建项目。在 $GOPATH/src 中,为了兼容性,Go 命令仍然在旧的 GOPATH 模式下运行。从 Go 1.13 开始,模块模式将成为默认模式。
export GO111MODULE=on
export GOPROXY=https://goproxy.io // 设置代理
$ go mod edit -require=”github.com/gin-gonic/gin@v1.1.4” // 修改 go.mod 文件
$ go tidy //下载更新依赖
因为:
在不使用额外的工具的情况下,Go的依赖包需要手工下载,
第三方包没有版本的概念,如果第三方包的作者做了不兼容升级,会让开发者很难受
协作开发时,需要统一各个开发成员本地$GOPATH/src下的依赖包
引用的包引用了已经转移的包,而作者没改的话,需要自己修改引用。
第三方包和自己的包的源码都在src下,很混乱。对于混合技术栈的项目来说,目录的存放会有一些问题
新的包管理模式解决了以上问题
自动下载依赖包
项目不必放在GOPATH/src内了
项目内会生成一个go.mod文件,列出包依赖
所以来的第三方包会准确的指定版本号
对于已经转移的包,可以用replace 申明替换,不需要改代码
问题一:依赖的包下载到哪里了?还在GOPATH里吗?
不在。
使用Go的包管理方式,依赖的第三方包被下载到了$GOPATH/pkg/mod路径下。
如果你成功运行了本例,可以在您的$GOPATH/pkg/mod 下找到一个这样的包 github.com/astaxie/beego@v1.11.1
问题二: 依赖包的版本是怎么控制的?
在上一个问题里,可以看到最终下载在$GOPATH/pkg/mod 下的包 github.com/astaxie/beego@v1.11.1 最后会有一个版本号 1.11.1,也就是说,$GOPATH/pkg/mod里可以保存相同包的不同版本。
版本是在go.mod中指定的。
如果,在go.mod中没有指定,go命令会自动下载代码中的依赖的最新版本,本例就是自动下载最新的版本。
如果,在go.mod用require语句指定包和版本 ,go命令会根据指定的路径和版本下载包,
指定版本时可以用latest,这样它会自动下载指定包的最新版本;
依赖包的版本号是什么? 是包的发布者标记的版本号,格式为 vn.n.n (n代表数字),本例中的beego的历史版本可以在其代码仓库release看到Releases · astaxie/beego · GitHub
如果包的作者还没有标记版本,默认为 v0.0.0
问题三: 可以把项目放在$GOPATH/src下吗?
可以。
但是go会根据GO111MODULE的值而采取不同的处理方式
默认情况下,GO111MODULE=auto 自动模式
auto 自动模式下,项目在$GOPATH/src里会使用$GOPATH/src的依赖包,在$GOPATH/src外,就使用go.mod 里 require的包
on 开启模式,1.12后,无论在$GOPATH/src里还是在外面,都会使用go.mod 里 require的包
off 关闭模式,就是老规矩。
问题三: 依赖包中的地址失效了怎么办?比如 golang.org/x/… 下的包都无法下载怎么办?
在go快速发展的过程中,有一些依赖包地址变更了。
以前的做法
修改源码,用新路径替换import的地址
git clone 或 go get 新包后,copy到$GOPATH/src里旧的路径下
无论什么方法,都不便于维护,特别是多人协同开发时。
使用go.mod就简单了,在go.mod文件里用 replace 替换包,例如
replace golang.org/x/text => github.com/golang/text latest
这样,go会用 github.com/golang/text 替代golang.org/x/text,原理就是下载github.com/golang/text 的最新版本到 $GOPATH/pkg/mod/golang.org/x/text下。
问题四: init生成的go.mod的模块名称有什么用?
本例里,用 go mod init hello 生成的go.mod文件里的第一行会申明
module hello
因为我们的项目已经不在$GOPATH/src里了,那么引用自己怎么办?就用模块名+路径。
例如,在项目下新建目录 utils,创建一个tools.go文件:
package utils
import “fmt”
func PrintText(text string) {
fmt.Println(text)
}
复制代码在根目录下的hello.go文件就可以 import “hello/utils” 引用utils
package main
import (
“hello/utils”
“github.com/astaxie/beego”
)
func main() {
utils.PrintText("Hi")
beego.Run() }
复制代码问题五:以前老项目如何用新的包管理
如果用auto模式,把项目移动到$GOPATH/src外
进入目录,运行 go mod init + 模块名称
go build 或者 go run 一次
关于Go 1.12的包管理介绍大致就到此了
根据官方的说法,从Go 1.13开始,模块管理模式将是Go语言开发的默认模式。
https://juejin.im/post/5c9c8c4fe51d450bc9547ba1
go.mod 目前有 4 个有效的 directives:module、require、replace 和 exclude。其中大多数场景只会用到 module 和 require。
module 的作用如上所示:提供标识和 import path。require 的作用非常显而易见:说明 Module 需要什么版本的依赖。
至于 replace 和 exclude 的作用,由于不算是一个高频动作(某些场景下 replace 用到的地方可能会相对多一些),所以下次再介绍。
go.sum 文件
go.sum 文件记录了 Module 中每一个特定版本依赖的加密后的 checksum。go.sum 的作用在于验证。比如 go mod verify 可以验证本地依赖的缓存是否与 checksum 吻合。go.sum 并非 lock file(比如 Cargo 或者 Dep 采用 lock file)。理论上,就算没有 lock file,Go Modules 所采用的 Minimal Version Selection 也能保证 reproducible build。
版本选择算法
Go Modules 选择了一种叫做 Minimal Version Selection 的算法(下称 mvs 算法)。这个算法简单来说就是:在所有列出的Module 集合中,总是选择满足全部依赖条件的最高版本的 Module。让我们来举例说明:
新增一个依赖 M,且此时 M 最新的版本是 v1.2.3,则此时 mvs 算法将使用 v1.2.3 的 M。如果 M 同时又依赖于 v1.0.0 的 D,mvs 算法将选择精确版本的 v1.0.0;
一个 Module 中存在两个依赖:Module A 依赖于 v1.0.0 的 D,Module B 依赖于 v1.1.1 的 D。此时 mvs 算法将选择 v1.1.1 的 D(因为 v1.1.1 的版本要高于 v1.0.0);
如何理解所谓的 Minimal ?这必须要和其他包管理工具对比着来看:
假如使用 Rust 的 Cargo 或者 Go 的 Dep,它们将总是使用当前新引入依赖的最新版本,并将其确定的依赖版本写入 lock file 中,下次编译直接使用 lock file 中所指定的版本即可。假如 Module A 依赖 Module B,且 Module B 依赖 Module C。此时 Cargo 或 Dep 将直接使用最新版的 B 和 C。但是 mvs 算法则会:使用最新版的 B 和 B 所指定版本的 C。假如此时有另外一个 Module D 需要使用更新版本的 C,mvs 算法才会使用更新版本的 C。
所以此处的 Minimal 指的就是:总是选择满足需求的最小依赖版本,而非每次都选择最新版本(除非用户显式指定),同时 mvs 算法也无需使用 lock file 来保证 reproducible build。mvs 算法是一种相对保守稳妥的策略,这也是 Go Modules 在设计之初与 Dep 的分歧之一。Russ Cox 觉得 Dep 所使用版本选择算法并不可靠,容易收到外部事件的干扰(比如又发布了新的版本),并非一个可以随着时间稳定的算法
设置 GOPROXY 环境变量,如:
1
$ export GOPROXY=mirrors.aliyun.com/goproxy
更多的 goproxy 站点可参考 Go Modules Wiki。
如果设置:
GOPROXY=off:Go Modules 将不允许从任何源下载依赖;
GOPROXY=direct:Go Modules 将直接从依赖的源地址进行下载操作;
启用对 vendor 目录的支持
如果因为某些原因,仍然需要使用 vendor,可以使用:
1
$ go mod vendor
将在当前项目中创建 vendor 目录。
默认 go build 将忽视 vendor 目录,若要在编译中使用 vendor 目录,可以:
1
$ go build -mod=vendor main.go
启用对 Go Modules 的支持
当安装完 Go 1.11 或更高版本(预计 1.13 版本后稳定成默认选项)之后,有如下两种方式启用 Go Modules:
在 $GOPATH/src 目录树之外创建目录,且当前项目的根目录中带有有效的 go.mod,编译时将自动启用 Go Modules。此时无需设置 GO11MODULE(或者显式设置为 auto);
如果是在 $GOPATH/src 路径下,显式设置 GO111MODULE=on 触发 go 命令使用 Go Modules;
在 Preliminary module support 中,文档描述了 go 命令在依赖管理上的 3 种模式:
设置 GO111MODULE=off(GOPATH mode)
此时 go 将使用 GOPATH 和沿用老的 vendor 机制;
设置 GO111MODULE=on(module-aware mode)
此时 go 将不使用 GOPATH;
设置 GO111MODULE=auto 或者不设置
如果在 GOPATH/src 之外,此时将自动使用 Go Modules 机制否则还是用老的机制;
如何发布 v2 及更高版本 Module
参考 v2-go-modules 的做法,v2+ 版本的 Module 意味着存在可能有 breaking change,所以最推荐的做法是将新 Module 放在以版本为后缀的目录中,例如:
github.com/googleapis/gax-go @ master branch
/go.mod → module github.com/googleapis/gax-go
/v2/go.mod → module github.com/googleapis/gax-go/v2
这样新版的 Module 也拥有了一个新的 sematic import path。
https://zhengyinyong.com/post/go-modules-in-10-minutes/
https://research.swtch.com/vgo
https://juejin.im/post/5c7fc2b1f265da2dac4575fc
解决 GFW 问题
因为一些原因,国内的网络访问不到 golang.org 上的库,好在大部分库在 github 上都有镜像,可以用 replace 命令设置镜像,下面是我碰到的一些库
replace (
cloud.google.com/go => github.com/googleapis/google-cloud-go v0.0.0-20190603211518-c8433c9aaceb
go.etcd.io/bbolt => github.com/etcd-io/bbolt v1.3.4-0.20191001164932-6e135e5d7e3d
go.uber.org/atomic => github.com/uber-go/atomic v1.4.1-0.20190731194737-ef0d20d85b01
go.uber.org/multierr => github.com/uber-go/multierr v1.2.0
go.uber.org/zap => github.com/uber-go/zap v1.10.1-0.20190926184545-d8445f34b4ae
golang.org/x/crypto => github.com/golang/crypto v0.0.0-20190605123033-f99c8df09eb5
golang.org/x/exp => github.com/golang/exp v0.0.0-20190510132918-efd6b22b2522
golang.org/x/image => github.com/golang/image v0.0.0-20190523035834-f03afa92d3ff
golang.org/x/lint => github.com/golang/lint v0.0.0-20190409202823-959b441ac422
golang.org/x/mobile => github.com/golang/mobile v0.0.0-20190607214518-6fa95d984e88
golang.org/x/net => github.com/golang/net v0.0.0-20190606173856-1492cefac77f
golang.org/x/oauth2 => github.com/golang/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sync => github.com/golang/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys => github.com/golang/sys v0.0.0-20190602015325-4c4f7f33c9ed
golang.org/x/text => github.com/golang/text v0.3.2
golang.org/x/time => github.com/golang/time v0.0.0-20190308202827-9d24e82272b4
golang.org/x/tools => github.com/golang/tools v0.0.0-20190608022120-eacb66d2a7c3
google.golang.org/api => github.com/googleapis/google-api-go-client v0.6.0
google.golang.org/appengine => github.com/golang/appengine v1.6.1
google.golang.org/genproto => github.com/google/go-genproto v0.0.0-20190605220351-eb0b1bdb6ae6
google.golang.org/grpc => github.com/grpc/grpc-go v1.21.1
)
GO 1.12 之后支持了一个新的环境变量 GOPROXY,用来设置依赖的代理地址,有两个共用的地址:社区的 goproxy.io 和又拍云的 goproxy.cn,亲测好用
export GO111MODULE=on
export GOPROXY=https://goproxy.io
https://studygolang.com/articles/25001?fr=sidebar
https://colobu.com/2019/09/23/review-go-module-again/
https://cloud.tencent.com/developer/article/1604866
https://blog.golang.org/migrating-to-go-modules
https://colobu.com/2018/08/27/learn-go-module/
https://tedmax100.github.io/2019/10/09/Go-go-mod-%E7%B5%82%E6%96%BC%E4%B8%8D%E6%9C%83%E5%86%8D%E8%A2%ABGOPATH%E7%B6%81%E6%AD%BB%E4%BA%86/
https://juejin.im/post/5c8e503a6fb9a070d878184a