Go依赖管理经历了3个阶段:
- 早期GOPATH
- 中期Go Vendor
- 最新Go Module
目前被广泛应用的是 Go Module,整个演进路线主要围绕实现两个目标来迭代发展:
- 在不同环境 (项目) 依赖的库的版本不同
- 控制并管理依赖库的版本
一、旧版本依赖管理——早期GOPATH
GOPATH
是一个环境变量,用来表明你写的 go
项目的存放路径,GOPATH
路径最好只设置一个,所有的项目代码都放到 GOPATH
的 src
目录下。
windows下环境配置内容:
- 系统变量GOPATH:D:\CODEFile01\GoPro
- path添加路径:go编译器路径 和 GOPATH对应文件夹路径
在GOPATH目录下新建三个文件:
- bin:用来存放编译后生成的可执行文件
- pkg:用来存放编译后生成的归档文件
- src:用来存放源码文件
在进行Go
语言开发的时候,我们的代码总是会保存在$GOPATH/src
目录下。在工程经过go build
、go install
或go get
等指令后,会将下载的第三方包源代码文件放在$GOPATH/src
目录下, 产生的二进制可执行文件放在 $GOPATH/bin
目录下,生成的中间缓存文件会被保存在 $GOPATH/pkg
下。
编写的代码在src目录下,并且go get进行下载的包也会放在src目录下,这样存在一个问题:
缺点产生:
- 项目A 和项B 依赖于某一 package 的不同版本 (分别为 `Package V1` 和 `Package V2` ) 。而 `src` 下只能允许一个版本存在,那项目A 和项B 就无法保证都能编译通过。
- 在 GOPATH 管理模式下,如果多个项目依赖同一个库,则依赖该库是同一份代码,无法做到不同项目依赖同一个库的不同版本。
二、过渡版本依赖管理——中期Go Vendor
面对早期GOPATH的依赖管理方式存在的弊端出现了Go Vendor的解决方案。
早期GOPATH依赖管理的项目结构:
- 环境变量GOPATH指定文件夹路径
- 手动创建 bin/ 、pkg/ 、src/ 三个文件夹
中期Go Vendor依赖管理的项目结构:
- 环境变量GOPATH指定文件夹路径
- 手动创建 bin/ 、pkg/ 、src/ 三个文件夹
- 新增vendor文件
vendor文件其实是一个目录,所在的该项目使用依赖包会以副本形式放在vendor目录下。这个时候,导入包会引入优先级:
- 当前项目存在 Vendor 目录,会优先使用该目录下的依赖
- 如果依赖不存在,则会从 GOPATH 中寻找
看到这里,因该会明白了与早期GOPATH管理方式的不同之处。
缺点产生:
- Vendor 无法很好解决依赖包版本变动问题和一个项目依赖同一个包的不同版本的问题。
- 项目A依赖项目B与项目C,项目B依赖项目D-V1,项目C依赖项目D-V2,这样底层同一个项目A底层依赖了同一个项目D的不同版本,一旦更新,又该如何指定不同的依赖版本则成了问题。
三、新版本依赖管理——最新Go Module
Go Module 自 Go1.11 开始引入,Go 1.16 默认开启。可以在项目目录下看到 go.mod
文件。
Go Module实现依赖管理三个要素:
- 配置文件go.mod用于描述依赖
- 代理Proxy实现中心仓库管理依赖库
- go mod 操作命令用于管理依赖库初始化、更新
module test // 依赖管理基本单元
go 1.18 // 原生库依赖原生的Go SDK版本
require ( // 单元依赖 标识命名:module路径 + 版本号
rsc.io/quote v1.5.2
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
rsc.io/sampler v1.3.0 // indirect
)
3.1 如何使用 go mod
使用 Go Modules 需要配置 GO111MODULE 环境变量,GO111MODULE 有三个值:off、on 和 auto(默认值):
- GO111MODULE=off :关闭 Go Modules,沿用旧版本的 GOPATH 方式,项目的依赖包依旧在 vendor 目录或者 $GOPATH/src 目录下进行查找。
- GO111MODULE=on :开启 Go Modules,项目的依赖包记录在 go.mod 文件中。
- GO111MODULE=auto : go 指令根据当前目录来决定是否启用 Modules 功能。
- 当项目处于 GOPATH/src 内,则会使用 GOPATH/src 的依赖包。
- 当项目处于 GOPATH/src 外,则使用 go.mod 里 require 声明的依赖包。
使用 go.mod 的流程:
-
在 $GOPATH/src 目录之外创建 Golang项目
-
初始化项目的go.mod文件:go mod init 项目名称
- 项目下属的子目录是不需要再次 init 的,整个项目的依赖都会组织在根目录的 go.mod 文件里。
-
导入外部包
-
package main import "rsc.io/quote" //引入第三方依赖模块 func Hello() string { return quote.Hello() }
-
运行程序时,会自动下载依赖包并更新 go.mod 文件
-
检查 go.mod 文件中自动更新的依赖清单
-
module test 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 )
- 其中 require 是一个关键字,此外还有 module、replace 和 exclude:
- module 语句:指定模块的名字(路径)
- require 语句:指定的依赖模块。
- replace 语句:可以替换依赖项模块。
- exclude 语句:可以忽略依赖项模块。
- 其中 require 是一个关键字,此外还有 module、replace 和 exclude:
-
-
3.2 go mod 的子命令
-
go mod init <project_name> : 初始化一个 Module
-
go mod download:下载项目依赖的 modules 到本地 Cache
- 目前所有模块版本数据均缓存在 $GOPATH/pkg/mod 和 $GOPATH/pkg/sum
-
go mod edit:编辑 go.mod 文件,选项有 -json、-require 和 -exclude
-
go mod edit # e.g. go mod edit -replace=golang.org/x/crypto@v0.0.0=github.com/golang/crypto@latest
-
-
go mod graph:以文本模式打印模块需求图
-
go mod tidy: 删除错误或者不使用的 Modules
-
go mod vendor:生成 Vendor 目录
-
go mod verify:验证依赖是否正确
-
go mod why:查找依赖
-
go clean -modcache:清理 Modules 缓存