为什么需要依赖管理
当下在做一个项目时,我们不可能在从零开始去实现所有的能力,过程中肯定会需要依赖一些现有的第三方库,另外,也会在不同的项目中去复用相同的代码,这些项目本身之外的都属于依赖。
npmpackage.jsonpiprequirement.txt
go mod
基于 GOPATH 的包管理
go modGOPATH
什么是 GOPATH
GOPATHGOPATHsrcgo getsrc
GOPATH // 通过环境变量指定的目录
├── bin // 编译生成的二进制文件
├── pkg // 预编译文件,以加快程序的后续编译速度
├── src // 所有的源代码
├── github.com
├ ...
GOPATH 包管理
GOPATH
go get
govendor 包管理
启用方式:export GO15VENDOREXPERIMENT=1
1.5 版本时,推出了 vendor 机制,就是每个项目的根目录可以有一个 vendor 目录,用来保存项目所依赖的包。当执行 go build 时,会优先从 vendor 目录查找依赖,如果没有则会再到 GOPATH 目录查找。 使用 govendor 方式,需要每个项目单独维护一份对应版本包的副本,但是仍然无法很好的控制依赖包的版本问题。
dep 包管理
通过网上的资料可以看到,Golang 官方曾经推出过实验性质的包管理工具 dep,但是细节有些不太清楚,感兴趣的可以去网上在查阅一下相关资料
go mod 包管理
go mod 是 Golang 1.11 版本引入的官方包依赖管理工具,用于解决没有记录依赖包具体版本的问题,更方便依赖包的管理。
从 Golang 1.13 版本开始,go mod 成为默认的包依赖管理工具,可以在生产环境使用
go mod 特性
相比之前的包管理,go mod 具有一下特性:
GOPATH$GOPATH/pkg/modpkgName + versiongo.modgo.sum
go mod 环境
GO111MODULE
- off:使用 GOPATH 和 govendor 机制的包管理
- on:启动 go mod 模式
- auto:在 GOPATH/src 之外,将自动使用 go mode 模式,否则还是使用之前的模式
go mod 常用命令
go mod init**go mod tidy**go.modgo mod vendorvendorgo build -mod=vendorgo mod download$GOPATH/pkg/mod/cachego mod verifygo mod whygo mod graph
go.mod 文件
go.mod 有以下几个关键词:
- go:标识当前模块的 Golang 语言版本
- module:指定包的名字
- require:指定依赖包
- replace:替换 require 中声明的依赖包
- exclude:忽略依赖包的版本
replace 的使用
替换 require 中的包
将 require 中的依赖包替换为指定的包,下面项目最终使用的 uuid 包由 v1.1.1 更换为了 v1.1.0。
require (
github.com/google/uuid v1.1.1
...
)
replace github.com/google/uuid v1.1.1 => github.com/google/uuid v1.1.0
替换无法下载的包
类似上面代码提到的,也可以整体替换整个包的镜像(host + package)
replace golang.org/x/text => github.com/golang/text latest
调试本地依赖包
replace (
github.com/google/uuid v1.1.1 => ../uuid
)replace (
k8s.io/api => ./staging/src/k8s.io/api
k8s.io/apiextensions-apiserver => ./staging/src/k8s.io/apiextensions-apiserver
k8s.io/apimachinery => ./staging/src/k8s.io/apimachinery
...
)
禁止被依赖
发布包后不想被其他项目依赖,如开源的 k8s,它的 go.mod 中 require 中的依赖有大量为 v0.0.0,由于这些版本都不存在,所以其他想要依赖 k8s.io/kubernetes 的项目就无法使用。 其内部是采用 replace 来获取真实的依赖包
module k8s.io/kubernetes
require (
...
k8s.io/api v0.0.0
k8s.io/apiextensions-apiserver v0.0.0
k8s.io/apimachinery v0.0.0
...
)
// 通过 replace 替换为真实的依赖包
replace (
k8s.io/api => ./staging/src/k8s.io/api
k8s.io/apiextensions-apiserver => ./staging/src/k8s.io/apiextensions-apiserver
k8s.io/apimachinery => ./staging/src/k8s.io/apimachinery
...
)
go.sum 文件
通过 go.mod 文件信息可以看出(只有 name + version),无法保证构建的一致性,下载的依赖包有可能被恶意篡改。所以引入 go.sum 文件,来记录每个依赖包的哈希值(SHA-256),在 build 时,如果安装的依赖和 go.sum 记录的不一致,则会中断。 一般情况下,对于每个依赖包在 go.sum 文件中会保存两条记录:
- 该依赖包所有文件的哈希值
- 该依赖包 go.mod 的哈希值(如果不存在,则没有该记录)
在更新 go.sum 之前,为了确保下载的依赖包是真实可靠的,go 命令在下载完依赖包后还会查询 GOSUMDB 环境变量所指示的服务器,以得到一个权威的依赖包版本哈希值。如果 go 命令计算出的依赖包版本哈希值与 GOSUMDB 服务器给出的哈希值不一致,go 命令将拒绝向下执行,也不会更新 go.sum 文件。