Go工程化(七) Go Module
注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
本系列为 Go 进阶训练营 笔记,访问 博客: Go进阶训练营, 即可查看当前更新进度,部分文章篇幅较长,使用 PC 大屏浏览体验更佳。
序
3 月进度: 04/15 3 月开始会尝试爆更模式,争取做到两天更新一篇文章,如果感兴趣可以拉到文章最下方获取关注方式。
本文将会分为两部分,第一部分会简单介绍一下 go module 的使用,算是一个简明教程,第二部分会重点介绍一下使用 go module 使用过程当中会遇到的一些坑的解决办法。
Go Module 简明教程
从 go 1.11 的初步支持,到 1.16 后的默认开启,go module 已经经历了 5 个版本,已然成为了创建 go 项目的首选包管理方式,这一趴我们就先看一下 go module 的基本使用。
使用 Go Module
初始化
1 |
|
执行上述命令会在当前目录下生成一个 go.mod
文件
1 |
|
添加依赖
执行 go get 包 添加添加对应库到 go mod 中
1 |
|
这时候会在 go mod 中添加如下信息
1 |
|
- 在 go get 的时候如果不手动指定版本信息,会自动拉取最新的版本的包
- 如果想要拉取指定版本可以通过
go get github.com/sirupsen/logrus@v1.7.0
的方式,支持@版本号
例如@v1.7.0
@分支名
例如@master
@commit tag
例如@6cff360233dc4457f1536e4f3df4e4e740fd3410
// indirect
表示,我们在代码中没有直接应用这个包
执行完 go get 命令之后还会在目录下生成一个 go.sum
文件
1 |
|
这个文件主要包含当前依赖的所有的包,像 go-difflib
我们没有直接依赖,但是我们依赖的 logrus
有依赖它,所以会列在这里
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
这一串是一个加密的哈希数据,用来保证这个版本一定是一致的,避免包的发布者删除了这个版本之后,修改代码重复发布
现在我们在代码中使用这个包试试
1 |
|
清理依赖
随着我们开发,可能会有一些包,之前依赖但是后面就不再依赖了,这个时候我们如果要清理哪些不再需要的依赖可以执行下面的命令来进行清理
1 |
|
发布 Go Module
Go 版本号
go 默认使用语义化的版本来表示版本号,基本的方式
vMAJOR.MINOR.PATCH
- 有破坏性变更的时候需要增加主版本号,也就是
MAJOR
,例如v1.0.0
->v2.0.0
- 当有新增的函数或者是 API 时,我们增加
MINOR
版本号,例如v1.1.0
->v1.2.0
- 当没有新的 feature 的时候,例如 bug 修复时,我们增加
PATCH
版本号,例如v1.1.1
->v1.1.2
- 有破坏性变更的时候需要增加主版本号,也就是
- 除此之外我们还可以在版本号后面使用
-
表示一些特殊的预发布版本例如 alpha beta 版本等v1.1.1-alpha
v1.1.1-beta.2
- 上面这种特殊的版本只会在手动指定的版本号的时候才会去获取它,默认情况或者是在更新版本的时候不会获取这些版本
- 为了兼容在 Go Module 出现之前的一些版本,你可能会看到还有一种比较特殊的版本号
v0.0.0-20170915032832-14c0d48ead0c
- 如果依赖的仓库从来没有发布过版本,就会以这种方式存在
v1 及之前版本的发布
1.0 之前的版本发布非常简单,只需要做两件事情
- 添加一个 LICENSE 文件(非必须)
- 使用
git tag v1.0.0
加一个版本 tag 即可
v2 及之后版本的发布
2.0 之后的版本发布就麻烦一些了,因为 Go Module 的限制,v2 之后的版本需要在 go.mod
中显示的指定 /v2
主版本好标识,用户在使用导入包的时候也必须加上这个版本标志,这个好处就是可以同时同时导入不同版本的包,但是在升级的时候就比较蛋疼了,必须要将所有文件的导入路径都修改一下
那么该如何发布新版本的包呢?官方推荐的操作是
- 我们先在当前目录下创建一个
v2
文件夹,应为这样可以兼容那些还在使用GOPATH
的用户,当然这不是必须的 - 然后我们再修改一下
go.mod
文件, 在包名后加上主版本号,例如module github.com/mohuishou/go-mod-example/v2
- 最后我们再使用
git tag v2.0.0
打一个版本并发布即可
Go Module 避坑指南
1. 拉取依赖很慢,有的包还拉取不到
Go 默认的 GOPROXY 配置是 proxy.golang.org
, 默认国内无法访问,我们可以配置国内镜像,推荐 goproxy.cn
或者 goproxy.io
1 |
|
1.16 之前 Go Module 并未默认开启还需要配置
1 |
|
2. 公司私有仓库包如何获取
Go 获取包的时候默认会走 PROXY,这个只要你们的库没有对公网发布,那就获取不到,可以通过设置环境变量解决
1 |
|
3. 依赖的包被自动升级
在 1.16 后, go build, go test, go get 等命令已经不会自动升级我们依赖的包了,但是在 1.16 之前,这个操作很难受。
首先,这个操作非常的反直觉,其次还有可能会导致非预期的 bug,虽然在 Go Module 的设计当中,主版本不变的情况下都应该保持向前兼容,但是很多知名的第三库都做不到这个,包括 Google 自己开发的 grpc,我们之前就出现过由于 grpc 版本自动升级导致的程序连接错误,必须要回退版本才行。
三个解决办法都可以解决:
- 升级 Go 版本到 1.16
- 使用
-mod=readonly
,例如go build -mod=readonly
- 在
go.mod
文件中使用replace
指令指定版本
4. 包的源代码仓库删库了怎么办?
这个其实在 Go Module 上还好一些,因为 Go Module 默认使用 Go Proxy 只要你使用的库的 LICENSE 和 GOPROXY 没有问题,一般都会有缓存
- 建议公司需要搭建一套自己的 GOPROXY
- 建议使用官方的 GOPROXY 或者是 goproxy.cn
5. logrus 包名问题
1 |
|
现在这个错误应该比较少了,但是我们碰到过很多次了,主要的原因就是 logrus 的作者改了 github 名字,从 Sirupsen
-> sirupsen
就导致了大量依赖 logrus 库的第三方库报错冲突,这个的解决方案也是使用 replace
在 go.mod
的最后加上这么一句就可以了
1 |
|
6. go.sum git merge 冲突
其实很多包管理都有类似的问题,解决方法一般情况下 git merge 合并后再重新执行 go mod tidy 清理一下即可
7. Go 版本升级无法使用新特性
举个例子,我从 go 1.15 升级到 1.16 想使用 embed,也就是静态文件打包的特性,但是我们发现升级 Go 之后执行还是会报错
1 |
|
这种情况修改一下 go.mod
文件中 go 1.15
到 go 1.16
即可
总结
关于 Go Module 的介绍就到这里了,说实话 Go 的包管理一直以来都是社区的痛点,从 GOPATH 到 Go Vendor 再到 dep
、 glide
等社区工具再到现在的 go mod 整体来说还是变得越来越好,特别是最新的 go 1.16 版本,如果没有其他问题的话还是建议升级的,仅默认 readonly
模式这一项就可以少很多事情
参考文献
- Go 进阶训练营-极客时间
- https://blog.golang.org/modules2019
- https://blog.golang.org/using-go-modules
- https://blog.golang.org/migrating-to-go-modules
- https://blog.golang.org/module-mirror-launch
- https://blog.golang.org/publishing-go-modules
- https://blog.golang.org/v2-go-modules
- https://blog.golang.org/module-compatibility
- Go mod 七宗罪
- golang 1.13 - 依赖管理从 dep 到 mod 踩坑 | 存档 Save&Load
- Go 填坑之将 Private 仓库用作 module 依赖 | 小木屋
关注我获取更新
猜你喜欢
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处,禁止全文转载