目录

GO Modules的解释和几个实验

GO Modules的解释和几个实验

GO Modules的解释和几个实验


1.前言

最近忙着写《MySQL InnoDB Cluster容器化部署实战(Openshift4平台)》文章,公司事情太多,一时半而难以推到微信公众号上。

在写关于MySQL Router故障转移时候,考虑到Oracle推荐的最佳实践部署,打算快速写一个go后端应用与MySQL Router部署在一起做HA测试。

不经意间看到文章内容是“VScode-go语言debug时遇到 go:cannot find main module 的问题"的解决办法,居然是增加一个"GO111MODULE": "off" ,这样会关闭GO Module功能,这种文章显然不负责任。

为此稍微写几句关于GO Modules的用法。

2.go语言安装

关于Golang的安装,直接访问https://golang.google.cn/dl/

根据操作系统平台选择合适安装文件,然后直接安装。Brew速度太慢,可以忽略。

安装程序会自动检测之前状态,自动选择卸载后再安装【本人采用Mac】

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201130201659584.png

1
2
3
4
5
6
7
8
9
# 升级前
$ go version 
go version go1.14.4 darwin/amd64
$ which go
/usr/local/go/bin/go

# 升级后为15.5
$ go version
go version go1.15.5 darwin/amd64

go的安装路径是/usr/local/go/bin

1
2
$ echo $PATH
/Library/Frameworks/Python.framework/Versions/3.8/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/go/bin

该路径下具体包括两个文件,分别是gogofmt

1
2
3
4
$ ll /usr/local/go/bin  
total 34768
-rwxr-xr-x  1 root  wheel    14M 11 13 02:37 go
-rwxr-xr-x  1 root  wheel   3.3M 11 13 02:37 gofmt

3.GO Modules

3.1 安装

这里采用gin来做范例

1
2
# 安装gin,安装时刻的当前最新版本是v1.6.3
go get -u github.com/gin-gonic/gin

GO Modules默认安装在用户目录下的~/go/pkg/mod/

实际上这个路径就是GOMODCACHE。

可以从go env查看

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/zhangcheng/Library/Caches/go-build"
GOENV="/Users/zhangcheng/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/zhangcheng/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/zhangcheng/go"
GOPRIVATE=""
GOPROXY="https://goproxy.cn,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/zt/8_l7w4rd0fd4m_jzh9m1sjl00000gn/T/go-build981245043=/tmp/go-build -gno-record-gcc-switches -fno-common"

前述通过go get -u下载了gin框架到本地后,查看具体信息。

可以看出GOMODCACHE目录下已经有了1.6.3版本的gin库。

1.4.0是几个月前我开发系统时候下载的gin库。

1
2
3
4
5
6
7
$ ll ~/go/pkg/mod/github.com/gin-gonic
total 0
dr-x------  20 zhangcheng  staff   640B  7 31 15:18 contrib@v0.0.0-20190526021735-7fb7810ed2a0
dr-x------  20 zhangcheng  staff   640B  8  2 17:09 contrib@v0.0.0-20191209060500-d6e26eeaa607
dr-x------  20 zhangcheng  staff   640B  9  6 11:02 contrib@v0.0.0-20200810162008-6dee08bf958e
dr-x------  61 zhangcheng  staff   1.9K  7 31 15:18 gin@v1.4.0
dr-x------  60 zhangcheng  staff   1.9K 11 30 20:24 gin@v1.6.3

3.2 .GO Modules使用

正如前言所述,使用VS Code编译go语言时候,会提示错误,例如

1
 "go: cannot find main module; see 'go help modules'\n",

或者在VS Code中提示

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201130204804721.png

【解决办法】

在当前项目目录下生成包管理文件。cj.com.cn/goginhello1/v0.1是项目包名称。

1
2
$ go mod init cj.com.cn/goginhello1/v0.1
go: creating new go.mod: module cj.com.cn/goginhello1/v0.1

具体的go mod init用法如下

1
2
3
4
5
6
7
8
$ go mod init                           
go: cannot determine module path for source directory /Users/zhangcheng (outside GOPATH, module path must be specified)

Example usage:
        'go mod init example.com/m' to initialize a v0 or v1 module
        'go mod init example.com/m/v2' to initialize a v2 module

Run 'go help mod init' for more information.

使用 GO Module 管理依赖后会在项目根目录下生成两个文件go.modgo.sum

下述go语言具体代码,引用gin库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")

保存后,对于VS Code编译器就会进行实时查找,就会发现本地GOMODCACHE下的gin-gonic module,如果找不到则会通过GOPROXY="https://goproxy.cn,direct"将库下载到本地。

1
2
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.3

将上述代码编译运行、验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 编译,监听在8080端口
~/Downloads/代码库  go run goginhello.go 
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

# 客户端请求,返回pong
$ curl http:/127.0.0.1:8080/ping
{"message":"pong"}%  

# 服务端返回
[GIN] 2020/11/30 - 21:10:32 | 200 |      52.174µs |       127.0.0.1 | GET      "/ping"

3.3 两个实验

go.mod文件记录了项目所有的依赖信息,查看go.mod。

1
2
3
4
5
module cj.com.cn/goginhello1/v0.1

go 1.15

require github.com/gin-gonic/gin v1.6.3

go.sum记录每个依赖库的版本和哈希值,查看go.sum

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod 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=

3.3.1 动态增加库引用

提前不下载GORM库,直接在VS Code编译器中动态中增加对GORM引用,看会发生什么事情。

1、代码中添加gorm库引用

1
2
3
4
import (
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

2、一旦保存,编译器go-code-check会自动查找本地GOMODCACHE是否包括此库。

可以看到编译器开始下载

1
2
3
4
5
go: finding module for package gorm.io/gorm
go: downloading gorm.io/gorm v1.20.7
go: found gorm.io/gorm in gorm.io/gorm v1.20.7
go: downloading github.com/jinzhu/now v1.1.1
go: updating go.mod: existing contents have changed since last read

3、验证:具体下载的路径仍是/Users/zhangcheng/go/pkg/mod下的gorm.io

1
2
3
$ ll /Users/zhangcheng/go/pkg/mod/gorm.io            
total 0
dr-x------  28 zhangcheng  staff   896B 11 30 21:50 gorm@v1.20.7

版本号是当前时刻最新版本1.20.7,可以通过GORM官网确认。

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201130222946487.png

4、如此同时,当编译器保存后,项目目录下的go.mod实时发生了变化

1
2
3
4
5
6
7
8
module cj.com.cn/goginhello1/v0.1

go 1.15

require (
        github.com/gin-gonic/gin v1.6.3
        gorm.io/gorm v1.20.7 // indirect
)

可以看出项目包管理文件中,增加了gorm.io/gorm v1.20.7的引用。

go.sum实时增加了相关内容

1
2
gorm.io/gorm v1.20.7 h1:rMS4CL3pNmYq1V5/X+nHHjh1Dx6dnf27+Cai5zabo+M=
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=

3.3.2 动态减少库引用

在直接在VS Code编译器中动态删除对某一个库的引用,是否会对项目目录包管理产生影响?

例如当我们删除对gorm库的引用

我们发现go.mod和go.sum并不会有所变化。

此时我们使用go mod tidy命令,就会发现go.mod和go.sum就会将原gorm有关部分进行了删除,即删除无用的module。

4.最后

GO Modules功能核心在于go.mod文件。

在新建工程中通过go mod init projectname,生成一个go.mod文件,同时生成一个go.sum记录每个依赖库的版本和哈希值,当然可以通过手动编辑go.mod中的require依赖项或执行go get自动发现、维护依赖。

GO Modules还有更多的功能,例如导入gitlab私有仓库,或者导入本地包等等,不再另行讨论。

by 2020.11.30 张诚