[go] Go Module 迁移与进阶
背景
Go依赖包的管理一直是个诟病,不管是google groups社区还是gopherchina一些会议和meetup,大家都会常聊到相关话题,以寻求最佳实践
。在没有官方唯一指定的版本管理器出来前,各种第三方工具可以说是百花齐放,glide
、govendor
、dep
。中间官方还推出了试验性的管理工具vgo
,由于发布时候作者已强调为实验版本,而且使用上并未真正解决痛点,生态也缺乏,因此实际上未能引起太多的影响。
伴随GO1.11版本发布的go module
成为官方钦点的包管理器,进入19年大家对于Go版本管理器的讨论已经日渐减少,不少公司开始生产上使用GoModule。去年年底,我厂新的Go项目已经采用GoModule,而原有线上项目到今年3月份才真正实现全部升级改造。
本文主要分享我厂在线上使用GoModule和旧项目升级改造中遇到的问题和解决方法。
基本操作
在Go1.11版本时候需要指定环境变量 GO111MODULE=on 以启用 go mod,否则使用的仍是原有的依赖实现.
GO111MODULE = on 强制使用go module
GO111MODULE = off 强制关闭go module
GO111MODULE = auto 自动选择,若项目在$GOPATH/src下,关闭go module,若不是则开启go module
- go mod init 创建一个新的module,并初始化[
go.mod
]文件,若原始项目已存在dep,glide等第三依赖工具,init会自动从原有工具进行迁移 - go mod tidy 更新与同步依赖项
- go list -m all 输出当前项目的所有依赖项
进阶使用
- go mod vendor 把依赖项包下载到目录下的
vendor
文件夹,对于墙和部分私有包可以使用该方式,搭配go build -mod=vendor
即可直接用目录下的依赖项进行编译 - go mod download 直接下载指定依赖项,并放入gomod cache文件夹
问题与解决
Q 依赖包下载慢
A: 这个问题从go get
开始就已经存在,核心方法都是使用代理。由于go get
走的是http协议,对于墙外的我们使用ss代理到就近服务器即可。回到go module
也存在该问题,但是目前已经有针对的代理加速服务。核心原理是在靠近用户侧部署加速缓存服务,类似CDN原理,实现依赖包下载的加速。
核心架构:
开发环境/CI环境
↓ (拉取+缓存)
代理服务器
↓ (拉取+缓存)
依赖包源站(github,gitlab,gopkg...)
目前支持go module
的代理服务方案有:
1. 微软的athens 支持多种持久化方案,支持纯内存存储加速
2. Goproxy 支持指定磁盘存储
我厂代理服务是基于Goproxy
的二次开发来实现,主要修改了底层通信的加密、缓存策略和存储。通过压缩备份支持快速扩展。
Q 引用包丢失
在迁移旧项目过程中我们遇到了依赖项的丢失,即通过
go mod download
已经无法找到源包,源项目地址404.
A: 事实上升级过程中我们遇到了几种引用包丢失情况,分别说说我们的解决方案:
情况1 依赖项为私有站点,404无法访问,github无备份,原工程$GOPATH/src下存在源码
解决: 通过直接拷贝依赖项目源码到缓存服务器的缓存目录中,强制增加缓存
情况2 依赖项为私有站点,404无法访问,github有备份
解决: 使用replace指令替换依赖项,go mod edit -replace 'honnef.co/go/errcheck=github.com/dominikh/go-tools/errcheck@2019.1.1'
,对于被墙的包也同样适用。替换后go mod tidy
即可.
情况3 依赖项为github,原项目已不维护和内容删除
解决: 首先先看项目的tag是否还存在,若存在直接指定tag.go mod edit -require="github.com/uber/jaeger-client-go@v2.16.0"
,若tag也不存在,可以尝试到gopkg.in找找是否有进行托管。如果有用 go get -u gopkg.in/XXX(原项目地址)
,然后进入到$GOPATH/src/gopkg.in/XXX
并创建一个github repo作为远端地址,把本地推送上作为一个备份地址即可(需要注意原项目的LICENSE)。
Q vscode无法使用go module进行代码跳转
在更新到go module之后部分同事发现新的开发环境中直接使用
go mod tidy
拉取的依赖项无法直接通过vsc
A: 由于像vscode和liteIDE等编辑器都是使用的go工具以实现,目前版本虽然官方已支持go module,但部分插件仍默认使用的$GOPATH/src进行代码搜索。具体更新进度可看https://github.com/golang/go/issues/24661。目前我们的解决方案是go mod vendor
到项目的vendor文件夹,让插件到vendor目录进行代码查找,并且利用go mod
把控的项目/vendor
目录中依赖项的版本。等后续包括配套插件和静态检查工具都全部支持go module
后再考虑去除vendor目录。
Tips
1.为缺失版本的依赖项增加版本号
在新项目go mod init
时候,往往我们会直接先go get -u
获取新项目再执行go mod tidy
,导致当前项目为最新的tag版本。我们实际使用过程中会更建议创建一个模板工程,里面go.mod
已经强制设置了部分包的版本,对于新包会使用go mod edit -require="github.com/uber/jaeger-client-go@v2.16.0"
进行设置。
设置版本号的好处是统一线上依赖项的版本,防止出现由于开发$GOPATH/src
和CI系统的依赖版本不同导致的编译错误和实际业务错误。