1.什么是Go Module?
简而言之,Go Modul是Go在1.12之后官方发布的包管理工具。
现代的语言都有很多好用的包管理工具,如 pip 之于 python,gem 之于 ruby,npm 之于 nodejs。然而 Golang 早期版本却没有官方的包管理器,直到 go1.5 才添加了实验性的 vendor。尽管官方的不作为(保守),还是无法阻止社区的繁荣。社区诞生了许多工具,比较有代表如 govender,glide,gopm,以及半官方的 dep 等。
2.Go Module带来的改变–淡化了GOPATH
在 go1.12 之前,安装 golang 之后,需要配置两个环境变量即GOROOT 和GOPATH。前者是 go 安装后的所在的路径,后者是开发中自己配置的,用于存放go 源代码的地方。在 GOPATH 路径内,有三个文件夹,分别是
- bin: go 编译后的可执行文件所在的文件夹
- pkg: 编译非 main 包的中间连接文件
- src: go 项目源代码
开发的程序源码则放在src里,可以在src里创建多个项目。每一个项目同时也是一个文件夹。
1.12之后,不需要人工配置,系统会以
xxxxxxxxxx
~/go
1
目录为默认GOPATH,可以简单理解为Maven的.m2目录。
3.关于package
golang 的所有文件都需要指定其所在的包(package),包有两种类型,一种是 main 包,使用 package main 在代码的最前面声明。另外一种就是 非main 包,使用 package + 包名 。main 包的可以有唯一的一个 main 函数,这个函数也是程序的入口。也只有 main 包可以编译成可执行的文件。 注意:若一目录中有下一级目录,可以认为是一个内嵌的包,这个包与当前包即使包名相同也不是一个包!包名的完整路径是从项目。如
xxxxxxxxxx
➜ ~ tree go-mod-test
go-mod-test
├── go.mod
└── src
├── main.go
└── pk1
├── pk1
│ └── pk1.go
└── pk1.go
3 directories, 4 files
1234567891011
src中有个pk1包,pk1包里面有个嵌入的pk1包。三个go文件如下:
xxxxxxxxxx
//pk1/pk1.go
package pk1
import "fmt"
func Pk1Func() {
fmt.Println("pk1.Pk1Func")
}
123456789
//pk1/pk1/pk1.go
package pk1
import "fmt"
func Pk1Func() {
fmt.Println("pk1.pk1.Pk1Func")
}
123456789
package main
import (
"go-mod-test/src/pk1"
pk1pk1 "go-mod-test/src/pk1/pk1"
)
func main() {
pk1.Pk1Func()
pk1pk1.Pk1Func()
}
123456789101112
注意:main.go在调用是两个pk1都用了全路径,由于连个包名字都是pk1,main在导入是冲突需要引入别名pk1pk1。
4.关于Module
原理就不赘述,大家留心main.go引入pk1用的
xxxxxxxxxx
go-mod-test/src/pk1
1
注意前面的『“go-mod-test”』这个准确说是当前项目的父Module。在项目路径里通过执行
xxxxxxxxxx
go mod init go-mod-test
1
来生成go.mod,即初始化Module,内容就两行,模块名及go版本。
xxxxxxxxxx
➜ go-mod-test cat go.mod
module go-mod-test
go 1.14
1234
此时我们在main.go加入依赖,如
xxxxxxxxxx
_ "github.com/gin-gonic/gin"
1
然后我们运行
xxxxxxxxxx
➜ go-mod-test go mod tidy
go: finding module for package github.com/gin-gonic/gin
go: found github.com/gin-gonic/gin in github.com/gin-gonic/gin v1.6.2
123
就是同该命令来更新依赖,添加需要的,移除多余的,更新结果保存在go.mod以及go.sum。
go.mod可以加入版本管理,go.sum不建议,避免跨平台校验可能出错,而且执行go mod tidy可以自动生成。
xxxxxxxxxx
➜ go-mod-test tree .
.
├── go.mod
├── go.sum
└── src
├── main.go
└── pk1
├── pk1
│ └── pk1.go
└── pk1.go
12345678910
go.mod文件比初始化后多了一行依赖记录,版本号是自动更新的,也可以手动修改该文件指定版本号。
xxxxxxxxxx
➜ go-mod-test cat go.mod
module go-mod-test
go 1.14
require github.com/gin-gonic/gin v1.6.2
123456
go.sum保存了依赖的传递以及其版本号、hash值
xxxxxxxxxx
➜ go-mod-test cat go.sum
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
...省略N行...
h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
12345678
到目前为止,我们用了两条命令,即
xxxxxxxxxx
go mod init module-name
go mod tidy
12
下面看看go mod 有哪些功能
xxxxxxxxxx
➜ go-mod-test go mod help
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
Use "go help mod <command>" for more information about a command.
123456789101112131415161718192021222324
原来命令也不多,除了刚才的两个另外关注
xxxxxxxxxx
go mod download
1
看名字指定是下载依赖到本地缓存,那本地保存在哪呢?我们想起了前面说到的默认GOPATH
xxxxxxxxxx
➜ ~ tree $GOPATH -L 4
/Users/alexhu/go
└── pkg
├── mod
│ ├── cache
│ │ └── download
│ ├── github.com
│ │ ├── davecgh
│ │ ├── gin-contrib
│ │ ├── gin-gonic
│ │ ├── go-playground
│ │ ├── golang
│ │ ├── google
│ │ ├── json-iterator
│ │ ├── leodido
│ │ ├── mattn
│ │ ├── modern-go
│ │ ├── pmezard
│ │ ├── stretchr
│ │ └── ugorji
│ ├── golang.org
│ │ └── x
│ └── gopkg.in
│ ├── check.v1@v0.0.0-20161208181325-20d25e280405
│ └── yaml.v2@v2.2.8
└── sumdb
└── sum.golang.org
└── latest
25 directories, 1 file
123456789101112131415161718192021222324252627282930
果然,都下载到了$GOPATH/pkg/mod目录下了!其它命令试试就知道了!这里提醒下
xxxxxxxxxx
go mod vendor
12
会在项目中建立vendor目录,将依赖下载在该目录中,目录结构类似前面说的的pkg/mod目录,整个项目打包的话方便没有网络也可以编译项目。一般情况下不要提交本目录到版本管理中。
6.编译项目
根据老习惯,建立个bin目录,用下面命令生产可执行文件,文件名默认同源文件名:
xxxxxxxxxx
➜ go-mod-test mkdir .bin
➜ go-mod-test go build -o ./bin/ ./src/main.go
➜ go-mod-test ./bin/main
pk1.Pk1Func
pk1.pk1.Pk1Func
12345
另外,我们将main.go源文件放到项目目录下,然后执行
xxxxxxxxxx
➜ go-mod-test cp src/main.go .
➜ go-mod-test go build -o ./bin/
12
这时发现bin中生产的可执行文件不是main而是包名go-mod-test!
7.Go Moudle被墙相关
7.1下载失败
go mod download下载失败包库,增加代理,在.bashr或.zshrc中增加如下export
xxxxxxxxxx
export GO111MODULE=on
export GOPROXY=https://goproxy.io
12
https://goproxy.io若不稳定可以用七牛的https://goproxy.cn,或网上搜下其它的代理服务器。
7.2校验失败
如果在运行go mod download,提示Get https://sum.golang.org/lookup/xxxxxx: dial tcp 216.58.200.49:443: i/o timeout,则是因为Go 设置了默认的GOSUMDB=sum.golang.org,这个网站是被墙了的,用于验证包的有效性,可以通过如下命令关闭:
xxxxxxxxxx
go env -w GOSUMDB=off
1
但不建议,建议的做法是设置 GOSUMDB=“sum.golang.google.cn”,这个是专门为国内提供的sum 验证服务
xxxxxxxxxx
go env -w GOSUMDB="sum.golang.google.cn"
1
7.3 Goland中设置
系统的GOPROXY在Goland中会覆盖,所以Goland中要重新设置:
xxxxxxxxxx
File-->Preferences-->Go-->Go Modules(vgo)-->Enable Go Modules(vgo) intergration-->proxy-->https://goproxy.cn,direct
1
即可。
8.项目组织
先看下重构后的本Demo项目结构
xxxxxxxxxx
➜ go-mod-test tree
.
├── Makefile
├── bin
│ ├── main
│ ├── submain1
│ └── submain2
├── go.mod
├── go.sum
└── src
├── main
│ └── demomain.go
├── pk1
│ ├── pk1
│ │ ├── pk1.go
│ │ └── pk1_test.go
│ ├── pk1.go
│ └── pk1_test.go
├── submain1
│ └── submain1-main.go
└── submain2
└── submain2-main.go
7 directories, 13 files
123456789101112131415161718192021222324
说明如下: 1.项目根目录下有模块信息,即go.mod。 2.main目录(包)为主模块,需要编译成可执行文件。 3.pk1及其子目录pk1可以理解为公共目录,如放项目的一下公共源文件,不编译成可执行文件。 4.submain1及submain2为子模块(如功能微服务),需要编译成可执行文件,可能引用公共模块里的单元文件。 5.xxx_test.go为测试文件,命名规则就是单元名加后缀_test。 6.Makefile,与make工具搭档是构建系统的鼻祖,简化构建。如本例中
xxxxxxxxxx
➜ go-mod-test make build
go build -o ./bin/ ./src/main/...
./bin/main
2020/03/30 17:46:15 hahahaha
2020/03/30 17:46:15 pk1.pk1.Pk1Func
go build -o ./bin/ ./src/submain1/...
./bin/submain1
2020/03/30 17:46:16 submain1.main()
2020/03/30 17:46:16 msg from pk1/Pk1Func(): haha
go build -o ./bin/ ./src/submain2/...
./bin/submain2
2020/03/30 17:46:16 submain2.main()
2020/03/30 17:46:16 msg from pk1/pk1/Pk1Func()): pk1.pk1.Pk1Func
12345678910111213
一个make build帮我们编译好了并执行了三个(子)项目。再如
xxxxxxxxxx
➜ go-mod-test make test
go test ./src/... -v
? go-mod-test/src/main[no test files]
=== RUN TestPk1Func
=== RUN TestPk1Func/msg
=== RUN TestPk1Func/ha
--- PASS: TestPk1Func (0.00s)
--- PASS: TestPk1Func/msg (0.00s)
--- PASS: TestPk1Func/ha (0.00s)
=== RUN TestAdd
=== RUN TestAdd/1+2
--- PASS: TestAdd (0.00s)
--- PASS: TestAdd/1+2 (0.00s)
PASS
ok go-mod-test/src/pk1(cached)
=== RUN TestPk1Func
=== RUN TestPk1Func/default
--- PASS: TestPk1Func (0.00s)
--- PASS: TestPk1Func/default (0.00s)
=== RUN TestSub
=== RUN TestSub/5-3
=== RUN TestSub/4-2
TestSub/4-2: pk1_test.go:40: Sub() = 2, want 1
--- FAIL: TestSub (0.00s)
--- PASS: TestSub/5-3 (0.00s)
--- FAIL: TestSub/4-2 (0.00s)
FAIL
FAILgo-mod-test/src/pk1/pk10.204s
? go-mod-test/src/submain1[no test files]
? go-mod-test/src/submain2[no test files]
FAIL
make: *** [test] Error 1
1234567891011121314151617181920212223242526272829303132
跑完了全部单元测试。 本文结束。
转:https://blog.csdn.net/dgatiger/article/details/105205190