如何理解GO MOD?


createdtime 20211021

updatedtime 20211021

author venki.chen


一、是什么
1. 定义,是做什么用的?

定义:

  • go mod能管理的依赖包的版本,能保证在不同地方构建,获得的依赖模块是一致的,集成在go tool中。go version >= 1.11,go1.13已经默认开启。
  • 模块是相关Go包的集合。modules是源代码交换和版本控制的单元。go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。

参数说明:

序号 名称 说明 备注
01 CGO_ENABLED 指明cgo工具是否可用的标识。 set CGO_ENABLED=1
02 GOARCH 程序构建环境的目标计算架构。 set GOARCH=amd64
03 GOBIN 存放可执行文件的目录的绝对路径。 set GOBIN=
04 GOCHAR 程序构建环境的目标计算架构的单字符标识。
05 GOEXE 可执行文件的后缀。 set GOEXE=.exe
06 GOHOSTARCH 程序运行环境的目标计算架构。 set GOHOSTARCH=amd64
07 GOOS 程序构建环境的目标操作系统。 set GOOS=windows
08 GOHOSTOS 程序运行环境的目标操作系统。 set GOHOSTOS=windows
09 GOPATH 工作区目录的绝对路径。 set GOPATH=D:\WORKSPACE\Gopro\FK
10 GORACE 用于数据竞争检测的相关选项。
11 GOROOT Go语言的安装目录的绝对路径。 set GOROOT=D:\WORKSPACE\Fighting\go
12 GOTOOLDIR Go工具目录的绝对路径。 set GOTOOLDIR=D:\WORKSPACE\Fighting\go\pkg\tool\windows_amd64
13 GOPROXY 设置拉取的代理。 set GOPROXY=https://goproxy.cn,direct
14 GO111MODULE go modules 功能的开关。 set GO111MODULE=auto
15 GOINSECURE go 动态获取模块的请求方式,默认HTTPS,为1时则为http。 set GOINSECURE = 1
16 GOSUMDB 设定的module校验数据库。 set GOSUMDB=.yewifi.com,.yunpiaoer.com,*.yunpiaoer.cn
17 GONOSUMDB 设定的module不需要校验的数据库。 set GONOSUMDB = *.yewifi.com
18 GONOPROXY 不通过代理GOPROXY拉去代码,直接通过传统的VCS(版本控制系统)拉取代码。 set GONOPROXY=*.yewifi.com
19 GOPRIVATE 设置GOPRIVATE来跳过私有库。 go env GOPRIVATE=.gitlab.com,.gitee.com
20 GOVCS 这是 GO1.16 版本才添加的,其主要作用是指定哪些模块使用哪些 VCS。 go env GOVCS=

补充说明:

  • GOPROXY

    • set GOPROXY=https://mirrors.aliyun.com/goproxy/#设置拉取的代理。七牛云的是:https://goproxy.cn,direct
    • set GOPROXY=file://本地路径 # 也可以从本地加载。
  • GONOPROXY

    • GONOPROXY,基于前缀的匹配方式运行,上图中指定了gitlab.com,也就是所有 gitlab.com 上的代码,不从 GOPROXY 服务器上去获取,全部通过传统 VCS 方式,直接去原代码服务器上拉取。
  • GO111MODULE

    • GO111MODULE=off,无模块支持,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。
    • GO111MODULE=on,模块支持,go命令行会使用modules,而一点也不会去GOPATH目录下查找。
    • GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形:
      • 当前目录在GOPATH/src之外且该目录包含go.mod文件,开启模块支持。
      • 当前文件在包含go.mod文件的目录下面。
  • GOINSECURE

    • GO 默认会发送 HTTPS 请求,如果服务器想用 HTTP 协议,可以通过环境变量 GOINSECURE 来处理,当 GOINSECURE 为 1 时,GO 就会使用 HTTP 协议。
  • GOSUMDB

    • GOSUMDB(go checksum database)是Go官方为了go modules安全考虑,设定的module校验数据库,服务器地址为:sum.golang.org。
    • 你在本地对依赖进行变动(更新/添加)操作时,Go 会自动去这个服务器进行数据校验,保证你下的这个代码库和世界上其他人下的代码库是一样的。
    • module 验证:Go1.13 版本开始加入模块 SUM 验证机制,默认所有 go 模块下载后都会验证其 hash 是否与线上( 默认:sum.golang.org 国内:sum.golang.google.cn)记录的一致。
      • 验证的过程可以通过环境变量 GONOSUMDB 和 GOSUMDB 来控制:首先来看 GOSUMDB 的配置,它指定了需要使用的线上数据库地址。因为默认使用的 sum.golang.org 在国内无法访问,如果配置使用的是 google 搭建的国内镜像,还可以配置为 off,代表禁用校验,即下载模块不进行哈希值的校验,彻底抛弃这个过程。使用中我不建议这样做,可以使用 GONOSUMDB 的环境变量去配置不需要验证的模块,比如私有模块肯定是不能通过验证的。GONOSUMDB 是通过前缀匹配的方式运行的。图中配置了 gitlab.com,那么所有以 gitlab.com 开头的包都不会进行 GO 的校验和检查。
  • GONOSUMDB

    • 用于配置不需要校验的模块。
    • 举例子:go env -w GONOSUMDB=*.example.com,wt.io/private这样的话,像 “http://git.example.com/xyzzy”, "wt.io/private/test"这些公司和自己的私有仓库就都不会做校验了。
  • GOPRIVATE

    • 相当于前面两个环境变量(GONOPROXYGONOSUMDB)的集合,配置了 GOPRIVATE 就相当于把前面的两个环境变量一起配置了。
2. 我的理解
  • 关于GOPROXY
    • 从字面意思就能看出,GOPROXY表示的是go的代理设置,之所以有这个环境变量,是因为go这种语言不像C语言,在C语言中,如果我们想要使用别人的第三方代码,一般有两种途径:而在go语言中,类似于java,可以在编程时,引入第三方代码的库地址,比如git仓库,然后在编译的时候,IDE会自动的拉取第三方库文件到当前工程。这样做虽然很方便,但是带来了一个问题:网速和限制,golang默认的GOPROXY是https://goproxy.io,这个是官方的设置,我们可以使用国内的代理,Windows下设置如下:,因为一些限制,我们不能很顺利的使用和下载这些仓库,这样就会导致下载缓慢或者失败,所以这个时候就需要一个代理来实现下载,这个代理就是中间商,可以跨过限制来访问。
    • GO 支持通过 GOPROXY 协议获取 GO 模块。模块是基于 HTTP 协议的,只会使用 HTTP 的 get 请求,并且使用标准的 HTTP 状态码进行调用。当使用的公共 GOPROXY 协议,其 GOPROXY 代理服务器默认都是没有用户名和密码的。但实际上如果需要搭建私有的,是可以支持 HTTP 基础授权,方式与前面一样,通过 .netrc 文件去配置用户名和密码。另外 GOPROXY 还有两点特性:
      • 第一:比起使用 VCS 方式直接去克隆,GOPROXY 获取模块的速度会更快,原因后面会详细说明。
      • 第二:可以解决模块不能访问的问题,比如 Golang 域名访问不了等问题,通过第三方搭建好的代理服务器即可访问下载到这些模块。
    • GOPROXY 的配置是通过 GOPROXY 环境变量来控制,配置的是代理服务器URL。代理服务器 URL 可以配置多个,通过逗号和管道符来进行分割,管道符和逗号的区别后面会举例讲解。
      • 通过固定的字符串 off 和 direct 可以代替 URL。off 禁止从任何来源去下载模块,把 GOPROXY 设置为 off 会禁止下载模块,只能使用本地模块,无论从 gitlab、github或其他地方的模块都不能下载。direct 代表直接从VCS上拉取,一般会作为备选方案。
    • export GOPROXY='https://proxy.golang.org,direct'以及go env -w GOPROXY='https://goproxy.cn|direct'
      • 第一个是 Linux 环境变量的语法,通过 export 来设置环境变量。前面配置了proxy.golang.org,这是 google 官方的 goproxy 的服务器,逗号之后指定了备选方案 direct。在 GOPROXY 服务器返回403和410状态码时,表示找不到模块。以逗号为分隔指定备选方案时只有当服务器返回了403或410状态码时,go get 会尝试使用备选方案,这里是从版本管理平台上去下载代码。
      • 第二个使用了另一种语法配置,go env -w 语法是 GO 自带的,GO1.13版本开始支持。它是可以跨平台使用的,通过这种语法,没有操作系统的差异,在 windows、Linux、max 上面,都可以通过该方式去配置 GO 相关的环境变量。示例中设置成了国内常用的 proxy 的地址:goproxy.cn。这里使用了管道符指定备选方案,管道符的意义是无论代理服务器返回了什么错误,即便不是 HTTP 的错误,如 GOproxy 服务器挂了返回500的错误,或者网络错误。都会尝试使用备选方案去下载模块。
# 设置镜像 注:其中 -w 表示 写 操作。
go env -w GOPROXY=https://goproxy.cn,direct
二、为什么
解决什么问题,可以带来什么好处?

优点:

  1. 它可以管理一个依赖库的多个版本同时存在。

缺点:

三、怎么用
1. 应用场景

操作命令说明:

序号 命令 说明
01 go mod download download modules to local cache(下载依赖包)
02 go mod edit edit go.mod from tools or scripts(编辑go.mod)
03 go mod graph print module requirement graph (打印模块依赖图(列表))
04 go mod verify initialize new module in current directory(在当前目录初始化mod)
05 go mod tidy add missing and remove unused modules(拉取缺少的模块,移除不用的模块)
06 go mod vendor make vendored copy of dependencies(将依赖复制到vendor下)
07 go mod verify verify dependencies have expected content (验证依赖是否正确)
08 go mod why explain why packages or modules are needed(解释为什么需要依赖)
  • go mod init # 初始化当前目录为模块根目录,生成go.mod, go.sum文件。
  • go mod download # 下载依赖包。
  • go mod tidy #整理检查依赖,如果缺失包会下载或者引用的不需要的包会删除。
  • go mod vendor #复制依赖到vendor目录下面。
2. 具体应用
  • replace 让原本依赖的 github.com/repo/pkg 包,实际使用 github.com/your-fork/pkg@version。
go mod edit -replace github.com/repo/pkg=github.com/your-fork/pkg@version
  • 清缓存
go clean -modcache
  • go.mod:依赖列表和版本约束。
  • go.sum:记录module文件hash值,用于安全校验。
3. 注意事项
  • go mod不推荐使用vendor,不要将vendor提交到版本控制。
  • 提交go.mod,可以忽略go.sum,因为会根据校验sum跨平台可能报错。
  • go mod 维护了两个主要文件 go.sum 和 go.mod:
    • go.mod 维护了项目的golang版本以及golang依赖的库。
    • go.sum 维护了项目依赖库的版本管理信息,类似 svn/git 的一个库版本记录,可以切换同一个库的不同版本。
4. 实战操作

–方式1:–begin–

第一步:初始化项目

# 随便找一个位置新建一个文件夹study go mod init 包名(模块名)
go mod init 包名

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

  • go.mod 提供了module, require、replace和exclude 四个命令:

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

第二步:添加依赖

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")
}

第三步:引入第三方包

# 第二步中对应的包
go get github.com/gin-gonic/gin

# 查看go.mod文件内容
module study

go 1.16

require github.com/gin-gonic/gin v1.7.4 // indirect

注意:go mod模式引入的第三方包,默认存放在GOPATH下面的pkg目录,注意查看GOPATH。

go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit,go 会自动生成一个 go.sum 文件来记录 dependency tree。

可以使用命令 go list -m -u all 来检查可以升级的package,使用go get -u need-upgrade-package 升级后会将新的依赖版本更新到go.mod * 也可以使用 go get -u 升级所有依赖。

  • go get 升级:

    • 运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)。
    • 运行 go get -u=patch 将会升级到最新的修订版本。
    • 运行 go get package@version 将会升级到指定的版本号version。
    • 运行go get如果有版本的更改,那么go.mod文件也会更改

使用replace替换无法直接获取的package:由于某些已知的原因,并不是所有的package都能成功下载,比如:golang.org下的包。

replace (
    golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)

–方式1:–end–

–方式2:–begin–【go mod使用】

第一步:初始化项目

# 随便找一个位置新建一个文件夹learn go mod init 包名(模块名)
go mod init learn

第二步:创建一个文件venki.go

package learn

import "fmt" 

// say Hi to venki
func SayHi(name string) string {
   return fmt.Sprintf("Hi, %s", name)
}

第三步:初始化mod

go mod init gitee.com/venki/go_study_accumulate

# go mod文件内容
module gitee.com/venki/go_study_accumulate

go 1.16

第四步:发布go.mod到仓库,供其他项目使用

git init

# 忽视规则文件
vim .gitignore

git add -A

git commit -m '初始化mod'

# github创建对应的repo
git remote add origin gitee.com/venki/go_study_accumulate.git

git push -u origin master

这个时候没有加tag,所以,没有版本的控制。默认是v0.0.0后面接上时间和commitid。如下:

[email protected]

第五步:使用tag,进行版本控制

git tag v1.0.0

git push --tags

第六步:维护v1.0.0分支

$git checkout -b v1
$git push -u origin v1

再切出一个分支,用于后续v1.0.0的修复推送,不要直接在master分支修复。

第七步:其他项目使用mod

# 在study项目使用
创建一个main.go文件

package main

import (
	"github.com/gin-gonic/gin"
	"gitee.com/venki/go_study_accumulate v0.0.0" # 注意此处引入mod
)

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")
}

第八步:加载go mod

go mod tidy

–方式2:–end–

四、思考点
1. 它山之石
  1. 这里面有个坑,就是设置为auto的时候并且在GOPATH/src下,如果该目录或者父目录存在go.mod, go.sum文件,则go mod也是启用的。(go1.13已经默认开启)。
  2. go mod虽好,但是费开发者的电脑磁盘,go mod拉下的包都会放当前目录的pkg/mod目录下面,意味着不同项目引用相同的包会重复下载,不像java的maven本地一个集中的目录,不重复下载。
  3. 注:
在使用go modules时,GOPATH是无意义的,不过它还是会把下载的依赖存储在$GOPATH/pkg/mod 中
也会把go install 的结果放在 $GOPATH/bin 中。

当modules 功能启用时,依赖包的存放位置变更为$GOPATH/pkg
允许同一个package多个版本并存,且多个项目可以共享缓存的module。
2. 可以攻玉
五、参考链接
  1. 说一说go mod
  2. go mod使用
  3. go语言:环境变量GOPROXY和GO111MODULE设置
  4. go标准命令详解0.14 go env
  5. 私有化仓库的 GO 模块使用实践
  6. GOSUMDB 设置私库
  7. GO ENV 的一些参数

Guess you like

Origin blog.csdn.net/qq_38721452/article/details/121167415