引入Go模块之后,我以为这就是最终解决方案了。我很快意识到事实并非如此。最近,大家开始提倡使用Go模块代理。在研究了利弊之后,我得出结论,这是近年来最重要的变化之一。何处此言?是什么使Go模块代理如此特别?

在Go模块中,如果你添加了新的依赖项或者在没有缓存过的新机器上构建Go模块,则它将(go get)下载go.mod中的所有依赖项,并将其缓存以用于进一步的操作。可以通过使用vendor/文件夹并编译时携带-mod=vendor参数来绕过缓存(以及下载依赖项)。

但是这两种方法都不甚完美,我们有更好的方案。

img

(不)使用vendor/文件夹的问题

如果使用vendor/文件夹,有以下缺点:

  • vendor/默认情况下,该go命令不再使用该文件夹(在模块感知模式下)。如果你不使用-mod=vendor参数,它将不会发挥作用。这烦人的问题驱使其他hacky解决方案来支持Go的旧版本(请参阅:在Travis CI中使用 go modules 并启用 vendor支持
  • vendor/文件夹,特别是对于大型项目,会占用大量空间。这增加了clone仓库所花费的时间。即使您认为clone仅执行一次,但大多数情况下并非如此。CI / CD系统通常会为每个触发器(例如“pull request”)clone仓库。因此,从长远来看,这将导致更长的build时间并影响团队中的每个人。
  • 添加新的依赖项通常会导致改变代码评审的*困难*程度。在大多数情况下,您必须将依赖项与实际的业务逻辑捆绑在一起,这使得难以进行改动。

可能你想说,那我跳过vendor/文件夹不就没事了?这也无济于事,你必须解决以下问题:

  • go会尝试从原先的仓库下载依赖项。但是任何依赖关系都可能在哪天就消失了(例如 left-pad包被删惨案)。

  • VCS可能已关闭(例如github.com)。在这种情况下,你的项目跑不起来了。

  • 有些公司不希望内部网络外部有任何传出连接。因此,删除 vendor/文件夹对他们来说是不行的。

  • 假设一个依赖项被发布为v1.3.0并且你go get拉取到本地缓存下来。然而,一旦依赖项的所有者通过推送具有同样 tag的恶意内容来破坏仓库,假如你的在一天没有拉取过缓存的赶紧的计算机上重新build的,那你这次就拉到了这个有恶意内容的版本。为了防止这种情况,您需要将go.sum文件与文件一起go.mod存储。

  • 有些依赖使用的是git之外的VCS,例如hg(Mercurial),bzr(Bazaar)或svn(Subversion)。并非所有这些工具都安装在你的电脑(或Dockerfile)上,这常常导致失败。

  • go get需要获取go.mod列出的每个依赖项的源代码以解决递归依赖项(这需要每个依赖项自己的go.mod文件)。这极大地减慢了整个构建过程的速度,因为这意味着它必须下载(例如git clone)每个存储库以获取单个文件

我们如何改善这种情况?

使用Go模块代理的优点

img

默认情况下,go命令直接从VCS下载模块。GOPROXY环境变量允许在下载源的进一步控制。设置这个环境变量,就会开启goGo模块代理

通过将GOPROXY环境变量设置为Go模块代理,可以克服上面列出的所有缺点:

  • 默认情况下,Go模块代理是缓存和永久存储所有依赖项(在不可变存储中)。这意味着您不再需要使用任何vendor/文件夹。
  • 摆脱vendor/文件夹意味着您的项目将不会在存储库中占用空间。
  • 因为依赖项存储在不可变的存储中,所以即使依赖项从Internet上消失了,也可以免受影响。
  • 一旦将Go模块存储在Go代理中,就无法覆盖或删除它。这可以让我们的代码免受有人在相同版本上注入恶意代码的攻击。
  • 不再需要任何VSC工具来下载依赖项,因为依赖项是通过HTTP提供的(Go proxy在后台使用HTTP)。
  • 下载和构建Go模块的速度明显更快,因为Go代理通过HTTP单独提供了源代码(.zip存档)go.mod。与从VCS进行提取相比,这导致下载花费更少的时间和更快的时间(由于更少的开销)。解决依赖关系的速度也更快,因为go.mod可以独立获取(而之前必须获取整个存储库)。Go团队对其进行了测试,他们发现在带宽高的网络环境下速度提高了3倍,带宽差的网络环境下速度提高了6倍!
  • 可以轻松地运行自己的Go代理,这可以让我们更好地控制构建pipeline的稳定性,因为不依赖版本库,可以防止VCS停机这种罕见情况。

如你所见,使用Go模块代理简直完美。但是我们如何使用它呢?如果你不想维护自己的Go模块代理怎么办?让我们研究许多替代选择。

如何使用Go模块代理

要开始使用Go模块代理,我们需要将GOPROXY环境变量设置为兼容的Go module proxy。有多种方法:

1。)如果GOPROXY没有设置,空或设置为directgo get会直接从VCS(例如github.com)的下载依赖:

GOPROXY=""
GOPROXY=direct

也可以将其设置为off,这表示不访问任何的网络。

GOPROXY=off

2.)您可以开始使用公共Go代理。您的选择之一是使用Go小组(*由Google维护*)中的Go代理。可以在这里找到更多信息:https : //proxy.golang.org/

要开始使用它,您只需设置环境变量:

GOPROXY=https://proxy.golang.org

其他公共代理有:

GOPROXY=https://goproxy.io
GOPROXY=https://goproxy.cn # proxy.golang.org 被墙了, 可以使用这个代替

3.)也可以运行多个开源实现并自己托管。例如:

这需要自己来维护。但可自己决定这个代理是通过公共互联网还是只能内部网络访问。

4.)可以购买商业产品:

5.)甚至可以传递一个file:///URL。由于Go模块代理是响应GET请求(没有查询参数)的Web服务器,因此任何文件系统中的文件夹也可以用作Go模块代理。

即将进行的Go v1.13更改(已经发布)

Go v1.13版本中的Go proxy会有一些变化,我认为应该强调:

  1. GOPROXY环境变量现在可以设置为逗号分隔的列表。在回退到下一个路径之前,它将尝试第一个代理。

  2. 默认值GOPROXYhttps://proxy.golang.org,directdirect token后的所有内容都会被忽略。这也意味着go get现在将默认使用GOPROXY。如果根本不想使用Go代理,则需要将其设置为off

  3. 引入了一个新的环境变量GOPRIVATE,是一个逗号分隔的支持glob匹配规则的列表。这可用于绕过GOPROXY某些路径的代理,尤其是公司中的私有库(例如:)GOPRIVATE=*.internal.company.com

(白话翻译:GOPROXY设置例如GOPROXY=https://goproxy.cn,direct,意思是,首先尝试从https://goproxy.cn进行下载,如果没有,启用direct,即从仓库的的源地址下载,比如 githu.com/spf13/viper 这个路径,会使用https://githu.com/spf13/viper 这个路径进行下载。GOPRIVATE变量相当于同时设置了GONOPROXYGONOSUMDB,即对某个指定的私有仓库,直接从私有仓库拉取而不走github,而且不对模块进行校验)

所有这些更改表明Go模块代理是Go模块的核心和重要部分。

结语

同时使用GOPROXY 公共和专用的网络有很大的优势。这是一个很棒的功能,可以与go命令无缝配合。考虑到它具有许多优点(安全,快速,存储高效),明智的做法是在项目或组织中用起来。在Go v1.13中,会默认开启,这是个受欢迎的进步,它改进了Go中依赖管理的状态。


原文链接

延伸阅读