Go Modules介绍

为了解决Go包管理的问题,Go从1.11开始加入了Go Modules这一新特性。让包的依赖和版本管理更加容易。

一个module可以理解为一个单独的包或者模块,module的根目录下会包含一个go.mod文件。go.mod文件中定义了该module被其它包importpath,同时也包含了该module中依赖哪些包,和这些包的版本号。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
Go 1.11需要设置GO111MODULE来开启module功能 
GO111MODULE=on,会开启使用module
GO111MODULE=auto默认值,会根据当前目前来决定是否开启module。如果当前目录在``GOPATH/src``之外且当前目录有``go.mod``文件或当前文件在包含go.mod文件的目录下面
从Go 1.13开始module被默认开启

创建一个module

GOPATH/src目录下创建一个空文件夹hello,然后在文件夹下创建hello.go文件:

package hello

func Hello() string {
    return "Hello, world."
}

写一个单测文件hello_test.go

package hello

import "testing"

func TestHello(t *testing.T) {
    want := "Hello, world."
    if got := Hello(); got != want {
        t.Errorf("Hello() = %q, want %q", got, want)
    }
}

运行go test命令,这个时候返回:

PS D:\Code\hello> go test
PASS
ok      _/D_/Code/hello 0.176s

可以看到当前的目录是在D:\Code\hello,不在GOPATH/src目录下,这时我们并没有创建module,但是go在运行时创造了一个假的module名称是:_/D_/Code/hello

接下来运行go mod init 命令,创建一个module并且运行单测:

go mod init example.com/hello
go: creating new go.mod: module example.com/hello

go test
PASS
ok      example.com/hello       0.177s

可以看到case运行通过,并且返回ok example.com/hello 0.177s就是是我们刚才创建的module。在目录下面也可以看多了一个go.mod文件它的内容是:

module example.com/hello

go 1.14

mod是在module的根目录下创建的,子目录的import path是module的path加上子目录的地址。

添加外部依赖

在hello.go文件中导入一个外部的包:

package hello

import "rsc.io/quote"

func Hello() string {
    return quote.Hello()
}

运行单测:

go: finding module for package rsc.io/quote
go: found rsc.io/quote in rsc.io/quote v1.5.2
--- FAIL: TestHello (0.00s)
    hello_test.go:8: Hello() = "你好,世界。", want "Hello, world."
PASS
ok      example.com/hello       0.177s

这时打开go.mod文件:

module example.com/hello

go 1.14

require rsc.io/quote v1.5.2

可以发现go会自动去找rsc.io/quote这个依赖的最新版本同时加入到go.mod文件中,再次运行单测:

PASS
ok      example.com/hello       0.177s

可以发现go第二次已经不需要去找rsc.io/quote。运行go list -m all 返回:

example.com/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0

可以看到引入外部依赖rsc.io/quote,也会带来它的间接依赖
除了go.mod文件,目录中也会增加一个go.sum文件内容如下:

golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

后面增加了每个外部依赖内容的hash值,go通过这些hash值去验证你下载依赖的正确性。

升级版本

通过上面的go list -m all可以看到 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c 我们用到的版本是v0.0.0-20170915032832-14c0d48ead0c。我们接下来升级它的版本:

go get golang.org/x/text
go: golang.org/x/text upgrade => v0.3.3
go: downloading golang.org/x/text v0.3.3

go test
PASS
ok      example.com/hello       0.199s

此时go.mod文件内容是:

module example.com/hello

go 1.14

require (
	golang.org/x/text v0.3.3 // indirect
	rsc.io/quote v1.5.2
)

可以看到golang.org/x/text v0.3.3 // indirect这个间接依赖已经被升级到v0.3.3了。

运行go list -m all 返回:

example.com/hello
golang.org/x/text v0.3.3
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0

移除没有使用的依赖

运行go mod tidy 可以删除没有使用的包

下载依赖到当前目录

go mod vendor 会下载依赖到vendor中,只会下载你代码中的依赖,不会下载所有go.mod中引用的依赖

老项目的迁移

打开老项目的目录一般是在GOPATH/src下面,然后运行

go mod init xxx

go build

需要注意的是迁移项目可能会出现包冲突的问题,会报类似

cannot load xxxx: ambiguous import: found xxxx in multiple modules:

这样的错误,这时可能需要再go.mod中手动指定这个包的版本号

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄