前言
作为官方的包依赖管理工具 gomod,已经被广泛的使用于各个开源项目中了。自从有了它,腰不酸手不疼了,敲代码也更有劲了......o(∩_∩)o 所以,今天我们就来认识认识 gomod ,看看它是如何来解决我们的包管理问题!
gomod 产生背景
在我们开发程序时,经常会引用到第三方开源框架提供的方法。按 Go 以前的做法会先将开源项目的最新代码通过 go get 下载本地的 GOPATH 目录。然后在需要使用的时候,就到对应的 GOPATH 目录去查找了。
然而,这里需要考虑一个问题,就是不同时间点 go get 下载下来的代码可能是不一样的,因为第三方开源框架总会进行代码升级,此时不能保证各个开发者的本地环境依赖包都是同一个版本。
而这就无形中增加了维护的成本,每次升级得手动删除,然后重新引用。
特别是在使用 docker 或 k8s 这种需要重新构建环境的发布方式时, 更得注意这个问题了,因为谁也不知道最新的依赖包修改了哪些内容,是否有破坏性变更存在。
此时,就该 go mod 登场了,它通过在 go.mod 这个文件里记录了当前项目里所有依赖包的 git 仓库地址以及对应的版本号,来解决了包依赖管理的问题,后续在构建编译时,就会根据对应的版本号去拉取依赖包。也就是说只要我们维护了 gomod 文件,依赖问题就不再是问题了!!!
gomod 文件的创建
命令: go mod init 项目名
比如,我的项目是 manage, 那么就可以这样使用:
go mod init manage
此时就会在当前项目下生成 gomod 文件:
注意, 如果当前的项目是要给外部使用的,最好是配合 git 仓库命名,比如
go mod init github.com/lincoln/manage
以便其他项目可以 go get 引用得到。
依赖包的版本生成
上面的命令只是生成一个 gomod 文件,至于依赖包的版本号信息暂时是还没有生成的,可以使用下面 2 个命令进行获取:
命令1:go get 包名
如果依赖包比较多,那么 go get 就比较麻烦了。可以使用另外一个命令:
命令2:go mod tidy
这个命令将会扫描所有我们 import 到的包,并生成对应的记录到 gomod 文件里。
此时,我们看到了一条依赖包以及版本号的信息记录。
这里的 v2.1.0 是因为引用的 go-cache 包在 github 上已经打标签了,所以有 v2.1.0 类似的字样出现,后面有 incompatible 是因为 go-cache 包的命名没有遵循官方规范,所以加了 incompatible 加以区分。
如果引用的包没有打过标签,那就有其他的版本记录生成规则, 比如
v0.0.0-20210501091049-10806f459f65
就表示版本号 + 日期 + 主分支最新的 commit 哈希值前缀。
此外,我们还发现除了 gomod 文件之外,还有 gosum 文件。此文件主要是用来记录依赖包的 hash 值,防止部署到新环境时,重新拉取的包与之前本地拉取的包不一致。
gomod 文件的使用技巧
1)引用分支的包
默认情况下,go mod tidy 会拉取主分支的最新代码作为版本记录。
如果我们有多个功能在同时开发,按常规操作,是需要新建各自的 feature 分支来开发的,而不会在主分支上直接开发的。
所以,当有其他模块需要引用分支代码时,我们就不能按常规操作 go mod tidy 了。
此时我们需要手动修改 gomod 引用包的版本名字,替换为对应的分支名,比如 gomod 文件改为:
require github.com/patrickmn/go-cache develop
然后再使用 go mod tidy 命令,Go 就会自动的去获取 develop 分支的最新代码了。(注:此处只是演示,并不真实存在 go-cache 的 develop 分支)
2)引用本地开发的代码
golang 是根据 gomod 文件来构建程序的,如果我们引用了其他项目代码,那每次就得先提交代码到 git 仓库,然后重新构建 gomod 文件才能引用到最新的代码。
为了能直接引用本地正在开发的包,又不频繁提交代码,我们可以使用下面这个命令
replace github.com/patrickmn/go-cache => 本地项目包的地址
这样就可以在构建项目时,本地联调了。等联调完毕,再一次性的提交代码到 git 仓库里。
3)查看依赖包的历史版本
使用 go mod tidy 命令时总会拉取最新版本的依赖包,但当我们只想 import 某个历史版本时,就可以使用下面的命令来获取历史版本号了:
go list -m -versions github.com/patrickmn/go-cache
执行结果:
github.com/patrickmn/go-cache v1.0.0 v2.0.0+incompatible v2.1.0+incompatible
然后当我们想引用 v1.0.0 时,就可以这样改写了:
require github.com/patrickmn/go-cache v1.0.0
需要注意的是,go list
某个包,必须得先在此项目中引用了对应的包。例如,当前项目如果没有引用 go-cache 包,则go list
是获取不到信息的。
go module 其他命令
go list -m all :列出当前项目包名以及所有依赖到的包
go mod vendor: 将引用的包都生成到当前项目的 vendor 包下,这样可以不用每次重新构建时去拉取对应的包,直接加入到自己的 git 代码仓库管理中, 直接 git pull 即可。
另外,有点要注意的就是,如果我们在 gomod 文件里手动添加了某个依赖包,但实际在项目里并没有使用到这个依赖包时,那么在执行 go mod tidy 构建时,就会自动删除这个依赖包的相关记录。
总结
go mod 的使用很简单,go mod init
、go mod tidy
,基本就能解决很多依赖问题了。这也是 Go 官方一直提倡的简洁、优雅。