共计 3108 个字符,预计需要花费 8 分钟才能阅读完成。
接上篇: Go Module 工程化实践(一):基础概念篇。
2. go get 取包原理篇
不论是否开启 Go Module 功能,go get 从版本控制系统 VCS 中取包的基础过程是类似的,除了在新的实现中不再循环拉取 submodule 子模块以外。
2.1 go get 基础取包流程
假设依赖包 github.com/liujianping/foo 不在本地,需要通过 go get 获取。发起以下命令:
$: go get github.com/liujianping/foo
命令发出后:
2.1.1 第一步,正则匹配出依赖包的查询路径
go get 可以指定具体包的 import 路径或者通过其自行分析代码中的 import 得出需要获取包的路径。但是 import 路径,并不直接就是该包的查询路径。在 go get 的源码实现中,包的查询路径是通过一组正则匹配出来的。也就是说,import 路径是必须匹配这组正则表达式的,如果不匹配的话,代码是肯定无法编译的。笔者就贴一下这组正则表达式中的 github 正则与私有仓库的正则:
// Github
{
prefix: “github.com/”,
re: `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[\p{L}0-9_.\-]+)*$`,
vcs: “git”,
repo: “https://{root}”,
check: noVCSSuffix,
},
// 省略其它 VCS…
// General syntax for any server.
// Must be last. 私有仓库将会使用该正则
{
re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?(/~?[A-Za-z0-9_.\-]+)+?)\.(?P<vcs>bzr|fossil|git|hg|svn))(/~?[A-Za-z0-9_.\-]+)*$`,
ping: true,
},
以包路径 github.com/liujianping/foo 为例,正则匹配后,得出的查询路径就是:
https://github.com/liujianping/foo
再结合 go-get 参数,向远端 VCS 系统发起 https://github.com/liujianping/foo?go-get= 1 请求。
2.1.2 第二步,查询得出包的远端仓库地址
包的远端仓库地址,可以通过 go get 请求的响应中的 go-import 的 meta 标签中的 content 中获取的。
$: curl https://github.com/liujianping/foo?go-get=1 | grep go-import
<meta name=”go-import” content=”github.com/liujianping/foo git https://github.com/liujianping/foo.git”>
例子中的包对应的远端仓库地址就是:https://github.com/liujianping/foo.git.
2.1.3 第三步,根据仓库地址 clone 到本地
虽然版本控制系统 VCS 本身就存在各类区别,但是一些基础操作大多类似。在 go get 中具体 clone 的过程会根据具体的 VCS 采用对应的操作。
2.2 go get 代理取包流程
了解了 go get 取包的基础流程后,说说 Go Module 功能开启后的完整流程。
开启 Go Module 后,go get 增加了一个新的环境变量 GOPROXY。该环境变量一旦开启,go get 就完全切换到新的取包流程,即 GOPROXY 流程,暂时就这么称呼吧。
在 GOPROXY 流程中,官方定义了一组代理接口, 请参考官方接口定义。
GET $GOPROXY/<module>/@v/list returns a list of all known versions of the given module, one per line.GET $GOPROXY/<module>/@v/<version>.info returns JSON-formatted metadata about that version of the given module.
GET $GOPROXY/<module>/@v/<version>.mod returns the go.mod file for that version of the given module.
GET $GOPROXY/<module>/@v/<version>.zip returns the zip archive for that version of the given module.
其实这组接口的定义就是 $GOPATH/pkg/mod/cache/download 中的文件系统。就是说,我们可以直接将此目录下的文件系统作为代理使用,如下命令:export GOPROXY=file:///$GOPATH/pkg/mod/cache/download/
关于 GOPROXY 代理服务,网上有很多实现,官方也推荐了几个。各有各的问题,只能这样说。因为,对于一些定制话的需求,例如:
私有仓库的权限问题
个别库的镜像国内无法访问等
完美的解决方案,暂时还没有。但是即使这样,我们还是可以根据具体的工程化需求构建企业内部的一套标准的流程来。具体方案,在下一篇工程实践篇中讲解。
2.3 私有仓库取包过程中的常见问题
私有仓库的取包过程中出现的问题大多集中在基础取包过程中。具体的异常又可能发生在 2.1.1~2.1.3 任一阶段。分别列举常见问题与解决思路。
2.3.1 私有仓库 clone 阶段的权限问题
通常情况下,私有仓库的访问是基于账号权限的。例如,private.vcs.com/group/foo 的包路径,在 go get 过程中,会正则匹配出 https://private.vcs.com/group/foo.git 的仓库路径,假设 VCS 系统是 gitlab 搭建的。
那么在 git clone https://private.vcs.com/group/foo.git 的过程中,系统会提醒用户提供用户名与登录密码。每次输入就会很累赘。
解决方案有二:
方法一:
增加 $HOME/.gitconfig 配置:[url “ssh://git@github.com/MYORGANIZATION/”]insteadOf = https://github.com/MYORGANIZA…
将原有的 https 访问方式替换成 ssh 方式。
方法二:
增加 $HOME/.netrc:machine github.com login YOU password APIKEY 将其中的 APIKEY 换成自己的登录 KEY。
虽然采用的 github 为例,但适用于 gitlab 服务。其实,还有一种解决方案,该方案,还能解决 2.3.2 中的问题,故在下节中讲解。
2.3.2 私有 VCS 非标路径问题
由于历史原因,笔者公司的 gitlab 服务地址就是非标准的路径,标准路径应该是:https://private.vcs.com,而笔者公司的 gitlab 路径则是:https://private.vcs.com:888.
如果按 go get 流程,import 包路径应该采用 d:private.vcs.com:888/group/foo,就可以正确匹配出该仓库的合理地址了。但是很不幸,在实际操作中,失败告终。具体原因读者可以自行测试一下。
此时唯一的办法,就是搭建一个中间服务:https://private.vcs.com 能够通过 go get 的包路径匹配查询正确的仓库地址。
其实该中间服务器的实现就非常简单。具体实现,笔者留到下一偏:工程实践篇 讲解。