Go
语言官方提供的依赖包管理工具已经发布很久了,有很多大佬的文章对Go Modules做了非常详尽的介绍,比如煎鱼大佬的: Go Modules终极入门(文章链接:https://juejin.cn/post/6844903433846145038)。
今天的文章我想跟大家聊一下我们项目在从govendor
迁移到Go Modules这个过程中总结的几点经验,如果你平时负责的项目早已开始使用Go Modules,这些内容可能对你来说有点小儿科。
环境变量设置
GO111MODULE
这个环境变量是Go Modules功能的开关,目前的默认值是auto,代表只要项目包含了go.mod
文件的话就自动开启Go Modules。如果之前没有设置过这个变量,这一步可以直接忽略,从其他包依赖管理工具向Go Modules迁移的时候,我们需要确认一下是不是以前在环境变量中设置过GO111MODULE=off
GOPROXY
Go Modules拉取依赖软件包的默认镜像源站点是https://proxy.golang.org
,由于众所周知的问题这个域名在国内访问受限,因此需要使用GOPROXY
环境变量设置国内的镜像站点。
GOPROXY=https://goproxy.cn
GOPRIVATE
这个环境变量主要是为项目依赖的一些公司内部的公共软件包准备的,一般是设置成代码仓库站点的域名。假如我公司所有项目都放在用GitLab
搭建的内部代码仓库站点,这个站点的域名是code.lazycorp.com
,那么我们就把这个环境变量设置成
GOPRIVATE=code.lazycorp.com
这样设置的话,所有路径前缀为code.lazycorp.com
的模块都不会再经过GOPROXY
指定的镜像站点拉取模块对应的软件包,转而去code.lazycorp.com
拉取软件包。
这里提一个小技巧,假如你自己开发的时候不想污染电脑系统里的全局环境变量,可以选择在GoLand
里开启Go Modules支持和设置环境变量,这样在GoLand
内编译运行程序时也能正常使用Go Modules。
GoLand配置截图
Replace完成版本替换
replace
指令的本意是在go.mod
文件里完成用一个模块版本替换已经require
的模块版本的功能,比如:
module code.lazycorp.com/buzz/practice // 这名是我瞎起的
go 1.13
replace(
google.golang.org/grpc v1.33.0 => google.golang.org/grpc v1.26.0
)
require (
google.golang.org/grpc v1.33.0
)
这里的replace
指令标识了,使用grpc
的v1.26.0
版本,替换require
里声明的v1.33.0
版本。
不过也正好有replace
命令,才能解决好几个软件包版本兼容的问题,我遇到的软件包兼容问题主要出在Etcd
和gRPC
上。
Etcd
比较诡异,它里面的bbolt
子库的模块名叫go.etcd.io/bbolt
但是自己的源码使用这个库时在代码里使用的import
路径却是github.com/coreos/bbolt
。再加上Etcd
提供的软件包与v1.30
版本以上的gRPC
相互之间不兼容,所以如果gRPC
使用了Etcd Naming
做服务发现和负载均衡,目前只能通过replace
指令对这两个模块进行版本替换。
replace (
github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5
google.golang.org/grpc v1.33.0 => google.golang.org/grpc v1.26.0
)
模块的版本控制
模块的版本号会与软件包的tag
相对应,比如上面的模块引用google.golang.org/grpc v1.26.0
对应于grpc
在GitHub
上代码仓库的名叫v.1.26.0
的标签。
注意标签的名字不能随便命名,比如类似v2020....
的标签,Go Modules是没法识别的。
Go Modules模块的版本格式为“主版本号.次版本号.修订号”,版本号的递增规则如下:
代码语言:javascript复制v1.26.0
| | |_ _ 修订号
| |
| |_ _ _ _ 次版本号
|
|_ _ _ _ _ 主版本号
- 主版本号:当你做了不兼容的更新时变更主版本号。
- 次版本号:当你做了向下兼容的功能性更新时更改次版本号。
- 修订号: 当你做了向下兼容的问题补丁修正时更改修订号。
测试和生产阶段的模块版本管理
假如我们对公司的公共包做了修改,那么怎么对公共包打标签呢?不能说在测试、仿真和生产阶段给都要给软件包打上不同版本的标签吧,这样代码仓库的标签管理起来容易混乱。
针对这种情况可以将版本信息追加到“主版本号.次版本号.修订号”的后面,作为延伸,比如:
代码语言:javascript复制// 在测试分支上打标签
v1.2.30-test
// 在仿真分支上打标签
v1.2.30-pre
这样等测试通过,引用公共软件包的项目需要上线的时候,就可以在公共软件包的master
分支打出v1.2.30
标签,将待发布项目中go.mod
文件里的引用更改成正式版本即可。
此外如果公共包的代码仓库上不存在任何标签,go get
默认拉取的是主干分支最新一次commit对应版本的代码,并且在go.mod
文件里为模块分配格式为 v0.0.0-主干分支最新一次commit的时间-commit哈希 这样的一个虚拟版本。
常用的命令
- go mod init 初始化
go.mod
文件,一般建新项目时才会用这个命令。 - go mod tidy 整理现有依赖,修改
go.mod
文件后执行会更新依赖。 - go mod graph 查看现有的依赖结构。
- go mod vendor 导出项目所有依赖到
vendor
目录 (不建议使用)。
这里列出go mod vendor命令是提醒大家,如果使用go mod vendor
, 会在项目里生成一个vendor
目录把所有依赖导出放到这里面。此后Go Modules在项目里会去vendor
里查找引用的依赖包而不是默认的$GOPATH/pkg/mod
目录。
假如我们主动更新了依赖包,还需要再次执行go mod vendor
把更新导出到vendor
目录,项目才能真正引用到更新后的依赖包。
所以既然开始使用Go Modules了,就应该尽量忘记vendor
,另外旧项目从govendor
改用Go Modules时的第一步也是先把项目里的vendor
目录删掉,再按照文章里的步骤进行操作 。
最后推广一下我自己写的Kubernetes教程,上次的文章《深入理解StatefulSet,用Kubernetes编排有状态应用》花了两个周末结果阅读惨淡。我写的Kubernetes文章都非常适合研发入门学习的啊
,大家有兴趣的话都去看看哈。
- END -