goproxy.io for Go modules

随着 go1.11 的发布,go 官方引入了 go module 来解决依赖管理问题,go module 被集成到原生的 go cmd 中,但是如果你的代码库在$GOPATH中,go1.11 的 module 功能是默认不会开启的,想要开启也非常简单, 通过一个环境变量即可开启go module:export GO111MODULE=on

关于 $GOPROXY

当我们使用go的时候,go默认会直接从代码库中去下载所需的相关依赖,GOPROXY 这个环境变量可以让我们控制自己从哪里去下载源代码,如果 GOPROXY 没有设置,go 会直接从代码库下载相关依赖代码。如果你像下面这样设置了这个环境变量,那么你就会通过 goproxy.io 下载所有的源代码。

export GOPROXY=https://goproxy.io

你可以通过置空这个环境变量来关闭,export GOPROXY= 。

以前大家执行 go get golang.org/x/net net代码库会下载到本地GOPATH中,以后有任何项目引用到了 golang.org/x/net 都不会再去下载这个代码库,因为本地GOPATH已经有了,哪怕版本不对,golang也会引用。但是随着 module 概念引入go语言,每个引入的 module 拥有了 version。随着代码库的不断更新迭代,大家即使是对同一个代码库的引用也可能用了不同的tag 或者 commit hash,基于这个现状,go1.11 的 module 会比以前更频繁的下载源代码。但是基于中国有中国特色的互联网,我们有时候很难get到我们需要的依赖源代码,进而导致项目编译失败,CI失败。于是,我们需要一个proxy。

The Download Protocol

当我们开启了 Go modules,Go 会决定去下载需要的依赖库。它会先在本地的cache($GOPATH/pkg/mods)中进行查找,如果本地不存在就会请求网络资源,比如github、gitlab、golang/x 等这些代码仓库。当我们设置了$GOPROXY 后就会从proxy server现在响应的代码库,但是这个proxy server极其简单,交互流程如下:

Go 会首先向proxy发起一个 GET 请求,询问可用的版本列表,请求地址为:/{module name}/@v/list, proxy server 会返回一个已经缓存在server本地的简单的文本列表:

v0.0.0-20180826012351-8a410e7b638d
v0.8.0
v0.7.1

接着,Go 根据需要去下载响应的版本,并向 proxy server 发起请求 /{module name}/@v/{module revision, proxy server 接受到请求后会读取本地缓存的info文件,并把数据以json格式返回:

type RevInfo struct {
    Version string    // version string
    Name    string    // complete ID in underlying repository
    Short   string    // shortened ID, for use in pseudo-version
    Time    time.Time // commit time
}

对于一个具体的请求,结果可能是这样的:

{
    "Version": "v0.0.0-20180826012351-8a410e7b638d",
    "Name": "v0.0.0-20180826012351-8a410e7b638d",
    "Short": "8a410e7b638d",
    "Time": "2018-08-26T01:23:51.436183-04:00"
}

然后Go会继续请求响应版本的mod文件,向 proxy server 发起 GET 请求:/{module name}/@v/{module revision}.mod. 然后根据上面的流程去获取依赖代码。

最后,GO会发起请求 GET 源代码zip包:/{module name}/@v/{module revision}.zip,当然还会校验zip包的 hash。

goproxy.io

goproxy.io 是一个开源项目,当用户请求一个依赖库时,如果它发现本地没有这份代码就会自动请求源,然后cache到本地,用户就可以从 goproxy.io 请求到数据。当然,这些都是在一个请求中完成的。goproxy.io 只支持 go module 模式。

得益于 go module 在设计的时候非常重视安全这个领域,所以在启用了 go module 后,你会发现除了 go.mod 这个文件之外,还有一个 go.sum 文件,这个文件保存了每个依赖库的对应的hash值,来保证下载回来的代码库是正确的,不被人篡改的。同时, goproxy.io 也是个开源的项目。可以自行部署到自己的IDC中,因为公司内部自己的代码库 goproxy.io 是无法访问到的。开源地址:https://github.com/goproxyio/goproxy

References: