其他分享
首页 > 其他分享> > 【Go语言入门】一文搞懂Go语言的最新依赖管理:go mod的使用

【Go语言入门】一文搞懂Go语言的最新依赖管理:go mod的使用

作者:互联网

一文搞懂Go语言的最新依赖管理:go mod的使用

2.1 背景

Go 依赖管理的演进经历了以下 3 个阶段:

在这里插入图片描述

目前被广泛应用的是 Go Module,整个演进路线主要围绕实现两个目标来迭代发展:

2.2 Go 依赖管理的演进

2.2.1 GOPATH

GOPATH 是 Go 语言支持的一个环境变量,是 Go 项目的工作区。其目录有以下 3 个结构 (需要手动创建文件夹):

image-20220517141135083

文件夹 作用
bin 项目编译的二进制文件
pkg 项目编译的中间产物
src 项目源码

2.弊端

下面的场景就体现了 GOPATH 的弊端:项目A 和项B 依赖于某一 package 的不同版本 (分别为 Pkg V1Pkg V2 ) 。而 src 下只能允许一个版本存在,那项目A 和项B 就无法保证都能编译通过。

image-20220517141844767

在 GOPATH 管理模式下,如果多个项目依赖同一个库,则依赖该库是同一份代码,无法做到不同项目依赖同一个库的不同版本。这显然无法满足实际开发中的项目依赖需求,为了解决这个问题,Go Vendor 出现了。

2.2.2 Go Vendor

image-20220517143602660

2.弊端

但 Vendor 无法很好解决依赖包版本变动问题和一个项目依赖同一个包的不同版本的问题。

image-20220517144202958

如图项目A 依赖 Package B 和 Package C,而 Package B 和 Package C 又依赖了 Package D 的不同版本。通过 Vendor 的管理模式不能很好地控制对于 Package D 的依赖版本。一旦更新项目,有可能出现依赖冲突,导致编译出错。归根到底: Vendor 不能很清晰地标识依赖的版本概念

2.2.3 Go Module

名称 作用
go.mod 文件,管理依赖包版本
go get / go mod 指令,管理依赖包

【终极目标】定义版本规则和管理项目依赖关系。和 Java 中的 Maven 作用是一样的。

2.3 Go Module实践

2.3.1 依赖管理三要素

要素 对于工具
配置文件,描述依赖 go.mod
中心仓库管理依赖库 Proxy
本地工具 go get / go mod

2.3.2 依赖配置-go.mod

打开项目目录下的 go.mod 文件,其文件结构主要分为三部分:

image-20220517150510319

【module 路径 (上图的“依赖管理基本单元”)】用来标识一个 module,从 module 路径可以看出从哪里找到该 module 。例如,如果以 github 为前缀开头,表示可以从 Github 仓库找到该 module 。依赖包的源代码由 Github 托管,如果项目的子包想被单独引用,则需要通过单独的 init go.mod 文件进行管理。

【原生库】依赖的原生 Go SDK 版本。

【单元依赖】每个依赖单元用 module路径 + 版本号 来唯一标识。

2.3.3 依赖配置-version

1.语义化版本

${MAJOR}.${MINOR}.${PATCH}
 

如:V1.18.1、V1.8.0

名称 含义
MAJOR 不同的MAJOR版本表示是不兼容的API。因此即使是同一个库,MAJOR版本不同也会被认为是不同的模块
MINOR 通常是新增函数或功能,向后兼容
PATCH 一般是修复bug

2.基于 commit 伪版本

每次提交 commit 后,Go 都会默认生成一个伪版本号:

v0.0.0-yyyymmddhhmmss-abcdefgh1234
 

如:v1.0.0-20220517152630-c38fb59326b7

名称 含义
v0.0.0 版本前缀和语义化版本是一样的
yyyymmddhhmmss 时间戳,提交Commit的时间
abcdefgh1234 校验码,包含12位的哈希前缀

2.3.4 依赖配置-indirect

2.3.2 节的 go.mod 文件图中,细心观察可以发现有些单元依赖带有 // indirect 的后缀,这是一个特殊标识符,表示 go.mod 对应的当前 module 没有直接导入的包,也就是非直接依赖 (即间接依赖) 。

image-20220517153440426

例如,一个依赖关系链为:A->B->C 。其中,A->B 是直接依赖;而 A->C 是间接依赖。

2.3.5 依赖配置-incompatible

2.3.2 节的 go.mod 文件图中,细心观察可以发现有些单元依赖带有 +incompatible 的后缀,这也是一个特殊标识符。对于 MAJOR 主版本在 V2 及以上的模块,go.mod 会在模块路径增加 /vN 后缀 (如下图中 example/lib5/v3 v3.0.2 )。这能让 Go Module 按照不同的模块来处理同一个项目不同 MAJOR 主版本的依赖。

image-20220517154220581

2.3.6 依赖配置-依赖图

如下图所示,Main 项目依赖项目A 和项目B ,且项目A 和项目B 分别依赖项目C 的 v1.3 和 v1.4 版本。最终编译时,Go 所使用的项目C 的版本为:v1.4 。

【总结】Go 选择最低的兼容版本。

image-20220517160311299

2.3.7 依赖分发-回源

image-20220517161158838

2.弊端

直接使用 GitHub 仓库下载依赖存在一些问题:

3.解决方案-Proxy

image-20220517183641065

2.3.8 GOPROXY的使用

image-20220517184332536

2.3.9 工具-go get

go get example.org/pkg +...
 

后面跟不同的指令能实现不同的功能:

指令 功能
@update 默认
@none 删除依赖
@v1.1.2 下载指定tag版本,语义版本
@23dfdd5 下载特定的commit版本
@master 下载分支的最新commit

2.3.10 工具-go mod

指令 功能
init 初始化,创建go.mod文件
download 下载模块到本地缓存
tidy 增加需要的依赖,删除不需要的依赖

在实际开发中,尽量提交之前执行下 go tidy ,减少构建时无效依赖包的拉取。

2.4 go mod使用

2.4.1 设置GO111MODULE

Win + R 输入 cmd 打开命令行,输入:

go env

即可看到 GO111MODULE (默认情况是空的):

image-20220518162705957

GO111MODULE 有三个值:off、on 和 auto (默认值)

【注】

设置的命令如下:

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

可在命令行中输入:go env 查看 GO111MODULE=on

2.4.2 清空所有GOPATH

开启 go mod 之后,并不能与 GOPATH 共存。必须把项目从 GOPATH 中移除,否则会报 $GOPATH/go.mod exists but should not 的错误。

在 Goland 中,移除项目所有 GOPATH 的操作如下:

image-20220518160417108

清空 GOPATH 之后,在单元测试模式下,同一个包下不同文件函数调用报错为 undefined 的问题也会解决。

2.4.3 在新项目中创建go mod

注意:如果你的项目根目录下已经有 go.mod 文件,可以不需要创建 go.mod 文件。
为了演示如何管理依赖,我创建了 hello.go 文件和 hello_test.go 单元测试代码:

单元测试的目录结构如下图所示:

image-20220518132408063

hello.go 代码:

package hello

import "rsc.io/quote"//引入第三方依赖模块

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

第 3 行代码:需要导入第三方依赖模块 rsc.io/quote

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

1.打开Windows终端命令行,cd 到新项目的文件夹目录。输入命令:

go mod init XXX(你的文件夹名称)

image-20220518171656640

成功创建了 go.mod 文件,如下图所示:

image-20220518165322088

2.【重点!】从 Go 1.16 开始,创建完 go.mod 文件还必须执行指令:

$ go mod tidy

来增加项目需要的最小依赖。否则,运行 go test 指令时会报 no Go files in G:\hellono required module provides package rsc.io/quote; to add it: go get rsc.io/quote 的错误。

运行结果如下图所示,go mod 会自动拉取项目所需的最小依赖。

image-20220518204236617

此时我们可以打开看看 go.mod 文件中的内容:

$ cat go.mod

输出:

module hello

go 1.18

require rsc.io/quote v1.5.2

require (
        golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
        rsc.io/sampler v1.3.0 // indirect
)

在 Goland 中可以双击直接打开:

image-20220518204405803

这样,就成功编写并测试了第一个模块了。

2.4.4 go.mod文件结构

2.4.5 添加依赖

在完成上述所有操作后,发现 hello.go 文件还是编译不通过,如下图所示:

image-20220518204513345

这时候我们再次执行 go test 指令,如下图所示:

image-20220518204907404

发现 go mod 会自动查找依赖并自动下载。单元测试通过。

【注意】此时我们已经开启go mod模式了,但Goland可能仍出现 hello.go 文件的 import 报红的情况。

【解决方法】如下图设置,Environment 处填写的 GOPROXY 网址要与cmd命令行输入 go env 中的 GOPROXY 保持一致。设置好后重启Goland即可。

image-20220519092159634
image-20220519092439474

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

image-20220518205257609

2.4.6 go get更新依赖

 

标签:GOPATH,依赖,语言,go,版本,Go,搞懂,mod
来源: https://www.cnblogs.com/lidabo/p/16386052.html