Go学习之Module介绍

最古老的GOPATH

Go的包管理方式是逐渐演进的,最初所有的包都放在GOPATH中,使用类似命名空间的包路径来区分包。
你可以将其理解为工作目录,在这个工作目录下,通常有如下的目录结构:

  • bin:存放编译生成的二进制文件
  • pkg:存放编译后的.a文件
  • src:存放项目的源代码,可以是自己写的代码。也可以是go get下载的包

在这种模式下,最严重的问题就是版本管理问题,因为GOPATH没有版本的概念,所以可能会遇到以下的问题:

  • 无法在项目中使用指定版本的包,因为不同版本的包的导入方法是一样的。
  • 其他人运行你开发的程序时,无法保证他下载的包版本是你所期望的版本。当其他人
  • 本地中,一个包只能保留一个版本,意味着你在本地开发的所有项目都是一个版本的包

vendor模式

为了解决GOPATH方案下不同项目下无法使用多个版本库的问题,GO1.5开始支持vendor。

以前使用GOPATH的时候,所有项目都共享一个GOPATH,需要导入依赖的时候,都来这里找。在GOPATH模式下,第三方库都只能有一个版本。

解决的思路是:在每个项目下都创建一个vendor目录,每个项目所需要的依赖都只会下载到自己的vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5的GO在设置了开启GO15VENDOREXPERIMENT=1
(注:这个变量在 v1.6 版本默认为1,但是在 v1.7 后,已去掉该环境变量,默认开启 vendor 特性,无需你手动设置)后,会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。

其搜索包的优先顺序为:

  • 当前包下的 vendor 目录
  • 向上级目录查找,直到找到 src 下的 vendor 目录
  • 在 GOROOT 目录下查找
  • 在 GOPATH 下面查找依赖包

虽然这个方案解决了一些问题,但是解决得并不完美。

  • 如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。
  • 并且如果要分享开源你的项目,你需要将你的所有的依赖包悉数上传,别人使用的时候,除了你的项目源码外,还有所有的依赖包全部下载下来,才能保证别人使用的时候,不会因为版本问题导致项目不能如你预期那样正常运行。

Go Modules

Go Modules在v1.11版本正式推出,在v1.14版本中,官方正式发话,称其已经足够成熟,可以应用于生产上。

从 v1.11 开始,go env 多了个环境变量:GO111MODULE ,这里的 111,其实就是 v1.11 的象征标志, go 里好像很喜欢这样的命名方式,比如当初 vendor 出现的时候,也多了个 GO15VENDOREXPERIMENT环境变量,其中 15,表示的vendor 是在 v1.5 时才诞生的。

GO111MODULE 是一个开关,通过它可以开启或关闭 go mod 模式。它有三个可选值:off、on、auto,默认值是auto。

  • GO111MODULE=off禁用模块支持,编译时会从GOPATH和vendor文件夹中查找包。
  • GO111MODULE=on启用模块支持,编译时会忽略GOPATH和vendor文件夹,只根据 go.mod下载依赖。
  • GO111MODULE=auto,当项目在$GOPATH/src外且项目根目录有go.mod文件时,自动开启模块支持。

go mod

golang提供go mod命令来管理Modules包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  test go mod
Go mod provides access to operations on modules.

Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.

Usage:

go mod <command> [arguments]

The commands are:

download download modules to local cache
edit edit go.mod from tools or scripts
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
verify verify dependencies have expected content
why explain why packages or modules are needed

命令 说明
download 下载依赖包
edit 下载依赖包
graph 编辑go.mod
init 打印模块依赖图
tidy 在当前目录初始化mod
vendor 拉取缺少的模块,移除不用的模块
verify 验证依赖是否正确
why 解释为什么需要依赖

使用

创建一个新项目

1
2
3
4
5
6
➜  test go mod init test
go: creating new go.mod: module test
➜ test ls
go.mod
➜ test cat go.mod
module test

go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。

go.mod 提供了以下四个命令:

  • module 语句指定包的名字(路径)
  • require 语句指定的依赖项模块
  • replace 语句可以替换依赖项模块
  • exclude 语句可以忽略依赖项模块

添加依赖

新建一个 server.go 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package 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 server.go运行代码会发现 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
➜  test go run server.go
go: finding github.com/labstack/echo v3.3.10+incompatible
go: downloading github.com/labstack/echo v3.3.10+incompatible
go: finding github.com/labstack/gommon/color latest
go: finding github.com/labstack/gommon/log latest
go: finding github.com/labstack/gommon v0.3.0
go: downloading github.com/labstack/gommon v0.3.0
go: finding golang.org/x/crypto/acme/autocert latest
go: finding golang.org/x/crypto/acme latest
go: finding golang.org/x/crypto latest
go: downloading golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
go: finding github.com/mattn/go-isatty v0.0.9
go: finding github.com/mattn/go-colorable v0.1.2
go: finding github.com/valyala/fasttemplate v1.0.1
go: finding github.com/mattn/go-isatty v0.0.8
go: finding golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a
go: finding github.com/valyala/bytebufferpool v1.0.0
go: finding golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
go: downloading github.com/mattn/go-isatty v0.0.9
go: downloading github.com/mattn/go-colorable v0.1.2
go: downloading github.com/valyala/fasttemplate v1.0.1
go: downloading golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3
go: downloading github.com/valyala/bytebufferpool v1.0.0

查看目录,发现多了go.sum文件。该文件主要用来记录 dependency tree。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
➜  test ls
go.mod go.sum server.go
➜ test cat go.mod
module test

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-20200622213623-75b288015ac9 // indirect
)
➜ test cat go go.sum
cat: go: No such file or directory
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=
...

当依赖都存在时,就会跳过下载的步骤。
可以使用命令go list -m -u all来检查可以升级的package,使用go get -u need-upgrade-package升级后会将新的依赖版本更新到go.mod * 也可以使用go get -u升级所有依赖

现在很少用GO,所以也没了解更深,这篇文章作为记录吧。剩下的后面在使用过程中再补充。。。。。