Go Module 工程化實踐(二):go get 取包原理篇
接上篇:Go Module 工程化實踐(一):基礎概念篇 。
2.go get
取包原理篇
不論是否開啟Go Module
功能,go get
從版本控制系統VCS
中取包的基礎過程是類似的,除了在新的實現中不再迴圈拉取submodule
子模組以外。
2.1go 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.2go get
代理取包流程
瞭解了go get
取包的基礎流程後,說說Go Module
功能開啟後的完整流程。
開啟Go Module
後,go get
增加了一個新的環境變數GOPROXY
。該環境變數一旦開啟,go get
就完全切換到新的取包流程,即
GOPROXY
流程
,暫時就這麼稱呼吧。
在
GOPROXY
流程
中,官方定義了一組代理介面, 請參考官方介面定義
。
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
代理服務,網上有很多實現,官方也推薦了幾個。各有各的問題,只能這樣說。因為,對於一些定製話的需求,例如:
- 私有倉庫的許可權問題
- 個別庫的映象國內無法訪問等
尚無完美的解決方案。但是即使這樣,我們還是可以根據具體的工程化需求構建企業內部的一套標準的GO Module
流程來。具體方案,在下一篇工程實踐篇
中講解。
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://[email protected]/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
的包路徑匹配查詢正確的倉庫地址。
其實該中間伺服器的實現就非常簡單。具體實現,筆者留到下一偏:工程實踐篇 講解。