Go Module 工程化實踐(二):取包原理篇

那一天我二十一歲,在我一輩子的黃金時代,我有好多奢望。我想愛,想吃,還想在一瞬間變整天上半明半暗的雲,後來我才知道,生活就是個緩慢受錘的過程,人一每天老下去,奢望也一每天消逝,最後變得像捱了錘的牛一樣。但是我過二十一歲生日時沒有預見到這一點。我覺得本身會永遠生猛下去,什麼也錘不了我。 -- 王小波 《黃金時代》

接上篇: Go Module 工程化實踐(一):基礎概念篇git

2. go get 取包原理篇

不管是否開啓Go Module功能,go get從版本控制系統VCS中取包的基礎過程是相似的,除了在新的實現中再也不循環拉取submodule子模塊之外。github

2.1 go get 基礎取包流程

假設依賴包github.com/liujianping/foo不在本地,須要經過go get獲取。發起如下命令:golang

$: go get github.com/liujianping/foo

命令發出後:正則表達式

2.1.1 第一步,正則匹配出依賴包的查詢路徑

go get能夠指定具體包的import路徑或者經過其自行分析代碼中的import得出須要獲取包的路徑。可是import路徑,並不直接就是該包的查詢路徑。在go get的源碼實現中,包的查詢路徑是經過一組正則匹配出來的。也就是說,import路徑是必須匹配這組正則表達式的,若是不匹配的話,代碼是確定沒法編譯的。筆者就貼一下這組正則表達式中的github正則與私有倉庫的正則:segmentfault

// 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爲例,正則匹配後,得出的查詢路徑就是:ssh

https://github.com/liujianping/foocurl

再結合go-get參數,向遠端VCS系統發起https://github.com/liujianping/foo?go-get=1請求。svn

2.1.2 第二步,查詢得出包的遠端倉庫地址

包的遠端倉庫地址,能夠經過go get請求的響應中的go-import的meta標籤中的content中獲取的。gitlab

$: 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代理服務,網上有不少實現,官方也推薦了幾個。各有各的問題,只能這樣說。由於,對於一些定製話的需求,例如:

  • 私有倉庫的權限問題
  • 個別庫的鏡像國內沒法訪問等

尚無完美的解決方案。可是即便這樣,咱們仍是能夠根據具體的工程化需求構建企業內部的一套標準的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://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的包路徑匹配查詢正確的倉庫地址。

更多文章可直接訪問我的BLOG:GitDiG.com

相關閱讀:

相關文章
相關標籤/搜索