背景

Go依赖包的管理一直是个诟病,不管是google groups社区还是gopherchina一些会议和meetup,大家都会常聊到相关话题,以寻求最佳实践。在没有官方唯一指定的版本管理器出来前,各种第三方工具可以说是百花齐放,glidegovendordep。中间官方还推出了试验性的管理工具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系统的依赖版本不同导致的编译错误和实际业务错误。

引用

文章目录