go包管理
1. 三种包管理方式
使用go path问题
- 代码开发必须在go path src目录下,不然,就有问题。
- 依赖手动管理
- 依赖包没有版本可言
从这个看, go path不算包管理工具
govendor
- 解决了包依赖,一个配置文件就管理
- 依赖包全都下载到项目vendor下,每个项目都把有一份。拉取项目时,开始怀疑人生。
go mod介绍
go modules 是 golang 1.11 新加的特性。现在1.12 已经发布了,是时候用起来了。Modules官方定义为:
模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。
2. go mod基本说明
下面设置go mod和go proxy
1 |
|
GOPROXY 和 GOPRIVATE
1. GOPROXY
作用:
- 指定模块代理服务器:
GOPROXY
环境变量用于指定 Go 模块代理服务器的 URL,Go 工具链通过这些代理服务器下载和缓存模块依赖。 - 提高下载速度和可靠性:通过使用代理,可以加速模块的下载过程,尤其是在原始源(如 GitHub)访问速度较慢或不稳定的情况下。
- 提供模块缓存:代理服务器缓存公共模块,减少重复下载,提高构建效率。
常见配置:
- 官方代理:
1
go env -w GOPROXY=https://proxy.golang.org
- 多个代理和直接访问:
1
go env -w GOPROXY=https://goproxy.io,direct
https://goproxy.io
:首选代理服务器。direct
:如果代理服务器无法提供模块,直接从源仓库获取。
注意事项:
- 多个代理顺序:Go 工具链会按照配置顺序依次尝试各个代理,直到成功获取模块或所有代理失败。
- 私有模块访问:默认情况下,
GOPROXY
会尝试通过配置的代理获取所有模块,包括私有模块,这可能导致访问失败或安全隐患。
2. GOPRIVATE
作用:
- 指定私有模块路径:
GOPRIVATE
环境变量用于指定不应通过公共代理获取的私有模块路径模式。 - 提高安全性:防止 Go 工具链通过公共代理查询和下载私有模块,保护私有代码的隐私和安全。
- 优化依赖管理:指定私有模块后,Go 工具链会直接从源仓库获取这些模块,避免不必要的代理查询,提升构建效率。
常见配置:
- 单个私有模块路径:
1
go env -w GOPRIVATE=git.garena.com/seatalk
- 多个私有模块路径(使用逗号分隔):
1
go env -w GOPRIVATE=git.garena.com/seatalk,example.com/private
注意事项:
- 结合
GOPROXY
使用:在配置GOPROXY
时,建议同时配置GOPRIVATE
,确保私有模块不会通过公共代理访问。 - 隐私保护:即使私有模块的访问权限设置正确,使用
GOPRIVATE
进一步确保不会意外通过代理泄露私有模块信息。 - 避免配置冲突:确保
GOPRIVATE
中指定的路径模式与GOPROXY
中配置的代理服务器逻辑不冲突,以避免依赖获取失败。
实际配置示例
假设你有一个私有仓库 git.garena.com/seatalk
,你可以这样配置环境变量:
1 |
|
总结
-
GOPROXY
:- 主要用于指定和管理模块下载代理服务器。
- 提高模块下载速度和构建效率。
- 需要谨慎配置以确保私有模块的安全访问。
-
GOPRIVATE
:- 专门用于指定私有模块路径,确保这些模块不会通过公共代理获取。
- 增强项目的安全性,保护私有代码不被泄露。
- 与
GOPROXY
配合使用,优化依赖管理流程。
最佳实践:
- 同时配置
GOPROXY
和GOPRIVATE
:确保公共模块通过代理获取,私有模块直接从源获取,避免安全和性能问题。 - 使用私有代理(如企业内部代理服务器):如果有大量私有模块,考虑部署内部代理以统一管理和优化模块下载。
- 定期更新和维护:保持
GOPROXY
和GOPRIVATE
的配置与项目需求同步,确保依赖管理的稳定性和安全性。
通过合理配置 GOPROXY
和 GOPRIVATE
,你可以有效地管理 Go 项目的依赖,提升构建效率,同时确保私有模块的安全性和隐私保护。
GO111MODULE
GO111MODULE 有三个值:off, on和auto(默认值)。
GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。
GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。
GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形:
1 |
|
当modules功能启用时,依赖包的存放位置变更为$GOPATH/pkg,允许同一个package多个版本并存,且多个项目可以共享缓存的 module
3. go mod命令
命令 | 说明 |
---|---|
download |
下载依赖包到本地缓存 |
edit |
从工具或脚本编辑 go.mod 文件 |
graph |
打印模块依赖图 |
init |
在当前目录初始化新模块 |
tidy |
拉取缺少的模块,移除不用的模块 |
vendor |
将依赖复制到 vendor 目录下 |
verify |
验证依赖是否正确 |
why |
解释为什么需要某些包或模块 |
初始化项目
在项目根目录下执行go mod init xxx
,go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。
go.mod 提供了module, require、replace和exclude 四个命令
module
语句指定包的名字(路径)require
语句指定的依赖项模块replace
语句可以替换依赖项模块exclude
语句可以忽略依赖项模块
添加依赖
在使用Go模块(Go Modules)管理依赖时,Go工具链遵循一定的原则来决定应该安装哪个版本的包(package)。下面是这些原则的详细解释:
- 安装包的原则
当你使用go get
命令来安装一个包时,Go会首先检查该包的所有公开发布的版本(通常是通过git标签来标记的版本)。Go的偏好是:
-
首先拉取最新的发布版本(release tag):这意味着如果一个库有一个或多个发布的版本,Go将会选择最新的版本。这些版本通常会遵循语义版本控制(SemVer),比如
v1.2.3
。 -
如果没有发布的版本,将拉取最新的commit:如果一个库没有任何发布的版本标签,Go将会使用该库的最新commit。这种情况下,依赖的版本可能不稳定,因为它直接反映了库在主分支(通常是
master
或main
)上的最新开发状态。
这种机制旨在确保项目能够利用依赖的稳定和经过测试的版本,同时也提供了一种方法来使用最新或未发布的更改。
`go get` 和 `go get -u` 的区别
go get
和 go get -u
的区别:
go get
:
go get
是用来下载并安装 Go 包的命令。如果没有特别指定版本,默认会安装包的最新稳定版本。
- 作用:下载并安装指定的包或模块。如果模块已经存在,则不会更新。
- 默认行为:下载的版本为当前模块指定的版本(可以通过
go.mod
指定版本)。
示例:
1 |
|
- 如果没有
-u
选项,go get
将下载最新版本,但如果你已经下载过该包,它不会去更新包版本。
go get -u
:
go get -u
会在执行 go get
操作时更新包及其依赖到最新版本(包括依赖的依赖)。
- 作用:除了下载指定的包外,还会更新该包及其依赖的所有子模块(包括你项目中直接依赖的模块的依赖)。
-u
选项:表示更新现有的依赖,甚至是它们的子依赖。如果该包已经安装,它会检查是否有更新,并将其更新到最新的可用版本。
示例:
1 |
|
- 使用
-u
后,如果gin
包有更新,go get
会自动更新为最新版本。 - 它会更新你所有的依赖,确保你的项目使用的依赖都是最新版本。
区别总结:
go get
只会安装或下载指定的包,不会更新已经存在的模块。go get -u
会更新指定包以及所有依赖包的版本,确保使用的是最新的版本。
适用场景:
go get
:适用于只需要安装指定版本的包,而不希望自动更新的情况。go get -u
:适用于你希望所有依赖都更新到最新版本时,特别是在你想要获取某个包的新功能或修复时。
go.sum
文件
当你使用Go模块工作时,Go工具会自动创建和维护一个名为go.sum
的文件。这个文件的作用包括:
-
记录依赖树(dependency tree):
go.sum
文件包含了项目所依赖的每个模块的精确版本和这些版本的哈希值。这不仅仅是直接依赖,还包括这些依赖所依赖的其他模块(即间接依赖)。 -
确保依赖的完整性和安全性:通过记录每个依赖版本的哈希值,
go.sum
文件帮助确保这些依赖在将来获取时仍然是一致的,未被篡改。这意味着无论何时何地构建项目,所使用的依赖都应该是完全相同的,从而提高了构建的可重复性和安全性。
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下的包。
modules 可以通过在 go.mod 文件中使用 replace 指令替换成github上对应的库,比如:
1 |
|
为什么使用 replace
?
- 处理无法访问的包:有些包,比如
golang.org/x/
下的包,在某些地区可能无法直接访问或下载。虽然这些包通常也在GitHub上有镜像,但Go工具链默认可能直接尝试从原始的golang.org
域名下载,导致失败。使用replace
指令可以将这些包的来源重定向到GitHub上的镜像地址,确保下载和使用的可行性。 - 开发和调试:如果你正在开发或修改某个依赖库,并希望在项目中测试它,你可以使用
replace
将依赖指向你本地的文件路径,而不是远程库。这样可以即时看到本地更改的效果,而无需推送更改到远程仓库。 - 依赖问题修复:有时候,一个库的特定版本可能包含错误或问题,而这些问题在另一个版本或fork中已经被修复。通过
replace
,你可以强制项目使用修复后的版本。 - 覆盖版本:当你需要使用一个特定版本的依赖,而不是由
go mod
解析的版本时,可以使用replace
来强制使用你指定的版本。
4. go mod发布和使用
Creating a Module
如果你设置好go mod了,那你就可以在任何目录下随便创建
1 |
|
在这个目录下创建一个文件say.go
1 |
|
初始化一个 go.mod
文件
1 |
|
查看 go.mod内容如下:
1 |
|
下面我们要将这个module发布到github上,然后在另外一个程序使用
1 |
|
执行完,上面我们就相当于发布完了。
如果有人需要使用,就可以使用
1 |
|
这个时候没有加tag,所以,没有版本的控制。默认是v0.0.0后面接上时间和commitid。如下:
1 |
|
官方不建议这样做,没有进行版本控制管理。
module versioning
使用tag,进行版本控制
making a release:
1 |
|
操作完,我们的module就发布了一个v1.0.0的版本了。
推荐在这个状态下,再切出一个分支,用于后续v1.0.0的修复推送,不要直接在master分支修复
1 |
|
5. go mod项目修改go版本
- 手动更新
直接在 go.mod
文件中修改 Go 的版本声明。例如,如果你想升级到 Go 1.17,你可以打开 go.mod
文件,并修改其中的版本行,如下:
1 |
|
这种方法简单直接,特别是当你清楚地知道你需要使用哪个 Go 版本时。
然后再执行
1 |
|
- 使用
go
命令
Go 命令提供了一种自动更新 go.mod
文件中 Go 版本的方式。运行以下命令:
1 |
|
这个命令不仅更新 go.mod
文件中的 Go 版本,还清理依赖项,确保所有依赖都是整洁的。这是一种更为全面的更新方式,因为它同时确保了依赖列表的准确性和最新状态。
只修改版本那一行
1 |
|
6. 修改 go mod init
最开始指定的项目名
项目名在 go.mod
文件中作为模块路径出现。如果你需要更改最初通过 go mod init
命令指定的项目名,可以直接编辑 go.mod
文件或使用 go mod edit
命令。
手动修改方式: 打开 go.mod
文件,直接编辑模块声明行:
1 |
|
命令行修改方式: 使用 go mod edit
命令修改模块路径:
1 |
|
7. 使用 replace
替换某个包
如果你需要在本地开发过程中替换某个包,或者指定某个依赖的特定版本或位置,可以在 go.mod
文件中使用 replace
指令。这同样可以通过手动编辑或使用命令行完成。
手动修改方式: 在 go.mod
文件中添加 replace
指令。例如,将 example.com/old/module
替换为本地路径:
1 |
|
或者将其替换为指定版本:
1 |
|
命令行修改方式: 使用 go mod edit
命令添加替换:
1 |
|
或替换为特定版本:
1 |
|
8. Vendoring
Vendoring 概述
Vendoring允许你将所有的外部依赖复制到你的项目内部(通常是vendor
目录)。这样做的主要好处是确保构建的可重复性,无论外部源的状态如何变化,你的项目都可以使用已经存储的、不变的依赖版本进行构建。
使用vendor
目录的好处
- 离线构建:在
vendor
目录中保留所有依赖的副本,可以在没有网络连接的情况下构建项目。 - 控制和稳定性:锁定依赖的具体版本,防止未经意的升级引入潜在的问题。
- 避免依赖消失:如果一个依赖库从公共存储库中消失,拥有本地副本意味着你的项目仍然可以构建和运行。
- 合规和审核:对于需要严格审计依赖来源和版本的环境,vendor目录提供了一个简单的解决方案。
如何使用vendor
目录
- 生成
vendor
目录
虽然go mod vendor
是正确的命令,但你提供的命令有误。正确的命令是:
1 |
|
这个命令会根据项目根目录下的go.mod
文件,将所有依赖复制到vendor
目录中。执行后,所有的模块依赖都会按照go.mod
文件中记录的具体版本,存放在项目的vendor
目录中。
- 构建时使用
vendor
目录
当你希望Go工具链在构建项目时使用vendor
目录中的依赖,而不是下载最新的或缓存的依赖,可以使用如下命令:
1 |
|
这个命令告诉Go编译器在构建时查找vendor
目录中的依赖。这是确保项目使用已验证和已审计依赖的一种方式,特别是在生产环境中部署时。
- 类似的
1 |
|
go mod vendor并不会将整个包放进vendor中
当你使用 go mod vendor
命令时,Go 会根据当前项目的 go.mod
文件中列出的依赖关系,将所需的依赖包的源代码复制到项目的 vendor
目录中。这个过程有几个关键点需要注意,以理解哪些文件会被放入 vendor
目录:
- 直接依赖的包
go mod vendor
主要将你的项目直接依赖的包放入 vendor
目录。这些是你在代码中直接引用的库,或者是由 go mod tidy
确认为项目必需的依赖。
- 间接依赖的包
对于那些被直接依赖的包间接引用的库(即传递依赖),只有实际被使用的部分会被复制到 vendor
目录中。Go 模块系统通过分析哪些包的哪些部分被实际使用,来减少 vendor
目录中不必要的文件。
- 剪裁
在将文件复制到 vendor
目录时,Go 不会复制整个包的所有文件。Go 会剪裁掉未被直接或间接引用的包的部分。例如,如果一个库有多个子包,但是你的项目只使用了其中一个子包,那么只有这个子包会被添加到 vendor
目录中。
- 构建标签和条件编译
如果某些依赖只在特定的操作系统或构建条件下被编译,go mod vendor
也会考虑这些情况。它只会包括适用于当前环境的那些文件。这意味着在不同的操作系统上运行 go mod vendor
可能会产生不同的 vendor
目录内容。
- 必要文件
go mod vendor
命令还会将依赖包中必要的非 Go 文件复制到 vendor
目录,如 LICENSE
文件、某些配置文件等。这是根据依赖包的 go.mod
文件中的指令以及其他元数据确定的。
使用 vendor
的目的
使用 vendor
目录的主要目的是确保项目的依赖在没有外部网络连接的情况下也能被构建(例如在一个隔离的环境中)。此外,它也可以确保依赖的版本在项目生命周期内保持不变,即使外部依赖源的版本发生了更新。
总结
go mod vendor
是一种管理依赖的方式,它提供了一种机制来包含项目所需的所有外部代码,但它并不包括依赖库的所有内容,只包括实际被使用的部分。这种方式有助于保持 vendor
目录的精简,避免不必要的依赖膨胀,并确保构建的可靠性和一致性。