前文
題記
《莊子》‘逍遙遊’有云:且夫水之積也不厚,則其負大舟也無力。覆杯水於坳堂之上,則芥爲之舟;置杯焉則膠,水淺而舟大也。
我們今天要介紹的就是北冥神功—go module絕技,以吸收他人內力爲己所用。而且別人的內力愈強 吸力愈大。極天下代碼於一身,好不快活。
前言
在上文中,我們介紹了
gopath
的含義、功能、優劣、以及如何通過GOPATH來組織項目在本文中,我們將介紹
go module
的原理和用法以試圖能夠回答下面的幾個問題go module 是什麼?
go module爲什麼需要?
go module的基本使用方法是什麼?
go module如何管理版本與依賴?
go module如何解決依賴的衝突問題?
go module 環境變量的配置與使用方式?
如何搭建私有 module鏡像?
go module 誕生的背景 & 爲什麼需要go module
在go module
之前,有一些問題長期困擾go語言的開發人員
能否將go工程代碼脫離
gopath
之外能否處理版本依賴問題並且自動選擇最兼容的依賴版本
能否使用go工具本地管理依賴項,自定義依賴項
go1.11開始支持,go1.13全面支持的
go modules
正是爲了解決上面的問題誕生的,下面我們詳細介紹go module
企圖解決的問題
解決`import` 路徑問題
在介紹
gopath
時,我們介紹瞭如果導入爲
import "github.com/gobuffalo/buffalo"
實際引用的是$GOPATH/src/github.com/gobuffalo/buffalo
文件中的代碼。
也就是說,在
gopath
中 ,導入路徑與項目在文件系統中的目錄結構和名稱必須是匹配的。那麼能否
import
路徑爲github.com/gobuffalo/buffalo
,但是項目實際的路徑卻是在另一個任意的文件目錄中?(例如/users/gobuffalo/buffalo
).答案是肯定的,go module
通過在一個特殊的叫做go.mod
的文件中指定模塊名來解決這一問題。
## go.mod
01 module github.com/gobuffalo/buffalo
02
...
06
在go.mod文件的第一行指定了模塊名,模塊名錶示開發人員可以用此來引用當前代碼倉庫中任何package
的路徑名,以此來替代$gopath
的路徑。從而,代碼倉庫在任何位置都已經沒有關係,因爲Go工具可以使用模塊文件的位置和模塊名來解析代碼倉庫中的任何內部import
。
解決代碼捆綁和版本控制
對於任何版本控制(VCS)工具,我們都能在任何代碼提交點打上"tag"標記,如下所示:
使用VCS工具,開發人員可以通過引用特定標籤將軟件包的任何特定版本克隆到本地。
當我們引用一個第三方包時,可能並不總是希望應用項目最新的代碼,而是某一個特定與當前項目兼容的代碼。對於某一個項目來說,可能並沒有意識到有人在使用他們的代碼,或者某種原因進行了巨大的不兼容更新。
我們希望能夠指明需要使用的第三方包的版本,並且go工具能夠方便下載、管理
更棘手的是,一個第三方包A可能引用了其他的第三方包B,因此還必須把第三方包A的全部依賴下載
如何查找並把所有的依賴包下載下來?
某一個包下載失敗應該怎麼辦?
所有項目之間如何進行依賴的傳導?
如何選擇一個最兼容的包?
如何解決包的衝突?
如果希望在項目中同時引用第三方包的二個不同版本,需要如何處理?
因此,只通過
gopath
維護單一的master包的方式是遠遠不夠的,因爲依賴包的最新代碼不一定與項目兼容。儘管go社區已經針對以上問題提供了一些解決方案(例如dep,godep,glide等)但是go官方的go moudle
提供了一種集成解決方案,通過在文件中維護直接和間接依賴項的版本列表來解決這一問題。通過將一個特定版本的依賴項看做是捆綁的不可變的依賴項,就叫做一個模塊(moudle)
go moudle 使用
Module緩存
爲了加快構建程序的速度並快速切換、獲取項目中依賴項的更新,Go維護了下載到本地計算機上的所有模塊的緩存,緩存目前默認位於$GOPATH/pkg
目錄中。有go的提議希望能夠自定義緩存的位置。
所在位置看上去如下所示:
go/
├── bin
├── pkg
├── darwin_amd64
└── mod
└── src
在mod目錄下,我們能夠看到模塊名路徑中的第一部分用作了模塊緩存中的頂級文件夾
~/go/pkg/mod » ls -l jackson@192
drwxr-xr-x 6 jackson staff 192 1 15 20:50 cache
drwxr-xr-x 7 jackson staff 224 2 20 17:50 cloud.google.com
drwxr-xr-x 3 jackson staff 96 2 18 12:03 git.apache.org
drwxr-xr-x 327 jackson staff 10464 2 28 00:02 github.com
drwxr-xr-x 8 jackson staff 256 2 20 17:27 gitlab.followme.com
drwxr-xr-x 6 jackson staff 192 2 19 22:05 go.etcd.io
...
當我們打開一個實際的模塊,例如github.com/nats-io
,我們會看到與nats庫有關許多模塊
~/go/pkg/mod » ls -l github.com/nats-io jackson@192
total 0
dr-x------ 24 jackson staff 768 1 17 10:27 gnatsd@v1.4.1
dr-x------ 15 jackson staff 480 2 17 22:22 go-nats-streaming@v0.4.0
dr-x------ 26 jackson staff 832 2 19 22:05 go-nats@v1.7.0
dr-x------ 26 jackson staff 832 1 17 10:27 go-nats@v1.7.2
...
爲了擁有一個乾淨的工作環境,我們可以用如下代碼清空緩存區。但是請注意,在正常的工作流程中,是不需要執行如下代碼的。
$ go clean -modcache
開始一個新的項目
我們從
GOPATH
外開始一個新的項目講解,新建一個新建夾以及一個main
文件
$ cd $HOME
$ mkdir mathlib
$ cd mathlib jackson@192
$ touch main.go
接着在當前目錄中,執行如下指令初始化moudle。
~/mathlib » go mod init github.com/dreamerjackson/mathlib
go mod init
指令的功能很簡單,自動生成一個go.mod
文件 後面緊跟的路徑即是自定義的模塊名。習慣上以託管代碼倉庫的URL爲模塊名(代碼將會放置在https://github.com/dreamerjackson/mathlib
下)go.mod
文件 位於項目的根目錄下,內容如下所示,第一行即爲模塊名。
module github.com/ardanlabs/service
#### 引入第三方模塊
go 1.13
接下來我們將書寫初始化的代碼片段
package main
import "github.com/dreamerjackson/mydiv"
func main(){
}
我們在代碼片段中導入了爲了講解go moudle
而特地的引入的packagegithub.com/dreamerjackson/mydiv
,其進行簡單的除法操作,同時又引入了另一個包github.com/pkg/errors
。其代碼如下圖所示:
如下圖所示,在goland中我們可以看到導入的package 是紅色的,因爲此時在go module的緩存並不能找到此package。
下載第三方模塊
爲了能夠將此package下載到本地,我們可以使用
go mod tidy
指令
$ go mod tidy
go: finding github.com/dreamerjackson/mydiv latest
go: downloading github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161
go: extracting github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161
同時我們在go.mod
中能夠看到新增加了一行用於表示我們引用的依賴關係
module github.com/dreamerjackson/mathlib
go 1.13
require github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161
注意在這裏間接的依賴(即
github.com/dreamerjackson/mydiv
依賴的github.com/pkg/errors
)並沒有也沒有必要在go.mod
文件展示出來,而是出現在了一個自動生成的新的文件go.sum
中.
## go.sum
github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161 h1:QR1fJ05yjzJ0qv1gcUS+gAe5Q3UU5Y0le6TIb2pcJpQ=
github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161/go.mod h1:h70Xf3RkhKSNbUF8W3htLNJskYJSITf6AdEGK22QksQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
使用第三方模塊
接着就可以愉快的調用我們的代碼了
package main
import (
"fmt"
"github.com/dreamerjackson/mydiv"
)
func main(){
res,_ :=mydiv.Div(4,2)
fmt.Println(res)
}
運行
go run
命令後,即會爲我們輸出除法結果2
手動更新第三方模塊
假設我們依賴的第三方包出現了更新怎麼辦?如果將依賴代碼更新到最新的版本呢?
有多種方式可以實現依賴模塊的更新,在
go.mod
文件中修改版本號爲:
require github.com/dreamerjackson/mydiv latest
或者
require github.com/dreamerjackson/mydiv master
獲取複製commitId 到最後
require github.com/dreamerjackson/mydiv c9a7ffa8112626ba6c85619d7fd98122dd49f850
還有一種辦法是在終端當前項目中,運行go get
go get github.com/dreamerjackson/mydiv
上述幾種方式在保存文件後,再次運行
go mod tidy
即會進行更新
此時如果我們再次打開go.sum
文件會發現,go.sum
中不僅僅存儲了直接和間接的依賴,還會存儲過去的版本信息。
github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161 h1:QR1fJ05yjzJ0qv1gcUS+gAe5Q3UU5Y0le6TIb2pcJpQ=
github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161/go.mod h1:h70Xf3RkhKSNbUF8W3htLNJskYJSITf6AdEGK22QksQ=
github.com/dreamerjackson/mydiv v0.0.0-20200305090126-c9a7ffa81126/go.mod h1:h70Xf3RkhKSNbUF8W3htLNJskYJSITf6AdEGK22QksQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
依賴移除
當我們不想在使用此第三方包時,可以直接在代碼中刪除無用的代碼,接着執行
$ go mod tidy
會發現go.mod
與go.sum
一切又都空空如也~
go module 最小版本選擇原理
每個依賴管理解決方案都必須解決選擇依賴版本的問題,當今存在的許多版本選擇算法都試圖識別任何依賴的“最新最大”版本。如果您認爲語義版本控制被正確應用並且將遵守約定,那麼這是有道理的。在這些情況下,依賴項的“最新最大”版本應該是最穩定和安全的版本,並且應與較早版本具有向後兼容性。
Go決定採用其他方法,Russ Cox花費了大量時間和精力撰寫和談論 Go團隊的版本選擇方法,即最小版本選擇(Minimal Version Selection,MVS)。從本質上講,Go團隊相信MVS可以爲Go程序提供最佳的機會,以實現兼容性和可重複性。我建議閱讀這篇文章,以瞭解Go團隊爲什麼相信這一點。
什麼是最小版本選擇原理
go最小版本選擇指的是選擇項目中最合適的最小版本。並不是說MVS不能選擇最新的版本,而是如果項目中任何依賴不需要最新的版本,則不需要它。
舉一個簡單的例子,假設現在項目
github.com/dreamerjackson/mydiv
的最新版本爲v1.0.2
,可通過下面指令查看所有
> go list -m -versions github.com/dreamerjackson/mydiv
github.com/dreamerjackson/mydiv v1.0.0 v1.0.1 v1.0.2 v1.0.3
假設現在有兩個模塊A、B,都依賴模塊D。其中
A -> D v1.0.1,
B -> D v1.0.2
如果我們的當前項目只依賴A,這個時候
go module
會如何選擇呢?像dep這樣的依賴工具將選擇v1.0.3,即最新的語義版本控制。但是在go module
中,最小版本選擇原理將遵循A項目聲明的版本,即v1.0.1如果隨後當前項目又引入了模塊B的新代碼怎麼辦?將模塊B導入項目後,Go會將項目的模塊D版本從v1.0.1升到v1.0.2。爲模塊D的所有依賴項(模塊A和B)選擇模塊D的“最小”版本,該版本當前處於所需版本集(v1.0.1和v.1.0.2)中
最後,如果刪除剛剛爲模塊B添加的代碼,會發生什麼?Go會將項目鎖定到模塊D的版本v1.0.2中。降級到版本v1.0.1將是一個更大的更改,而Go知道版本v1.0.1可以正常運行並且穩定,因此版本v1.0.2仍然是“最新版本”。
驗證最小版本選擇原理
爲了驗證最小版本選擇原理,作者嘔心瀝血設計了一個簡單的示例。
以項目github.com/dreamerjackson/mydiv
爲例,讀者可以將其看做上節中的模塊D
,其v1.0.1
與v1.0.2
版本的代碼如下,只是簡單的改變了錯誤返回的字符串。
## v1.0.1
package mydiv
import "github.com/pkg/errors"
func Div(a int,b int) (int,error){
if b==0{
return 0,errors.Errorf("new error b can't = 0")
}
return a/b,nil
}
## v1.0.2
package mydiv
import "github.com/pkg/errors"
func Div(a int,b int) (int,error){
if b==0{
return 0,errors.Errorf("new error b can't = 0")
}
return a/b,nil
}
接着
模塊B
即github.com/dreamerjackson/minidiv
引用了模塊D即github.com/dreamerjackson/mydiv
v1.0.1版本
## 模塊B
package div
import (
"github.com/dreamerjackson/mydiv"
)
func Div(a int,b int) (int,error){
return mydiv.Div(a,b)
}
最後當前的項目,我們將其稱爲
模塊Now
直接依賴了模塊D v1.0.2,同時依賴了模塊B當前代碼如下:
package main
import (
"fmt"
div "github.com/dreamerjackson/minidiv"
"github.com/dreamerjackson/mydiv"
)
func main(){
_,err1:= mydiv.Div(4,0)
_,err2 := div.Div(4,0)
fmt.Println(err1,err2)
}
當前的依賴關係如下:
當前模塊 --> 模塊D v1.0.2
當前模塊 --> 模塊B --> 模塊D v1.0.1
因此我們將驗證,是否和我們所料,當前項目選擇了模塊D v1.0.2 呢?
驗證方式有兩種:第一種爲直接運行,查看項目採用了哪一個版本的代碼
$ go run main.go
v1.0.2 b can't = 0 v1.0.2 b can't = 0
如上所示,輸出的結果全部是我們在模塊D v1.0.2中定義的代碼!
第二種方式是使用
go list
指令
~/mathlib » go list -m all | grep mydiv
github.com/dreamerjackson/mydiv v1.0.2
我們還可以通過使用go mod mhy
z指令,查看在哪裏引用了包github.com/dreamerjackson/mydiv
~/mathlib » go mod why github.com/dreamerjackson/mydiv
# github.com/dreamerjackson/mydiv
github.com/dreamerjackson/mathlib
github.com/dreamerjackson/mydiv
查看直接和間接模塊的當前和最新版本
我們可以使用
go list -m -u all
指令查看直接和間接模塊的當前和最新版本
~/mathlib » go list -m -u all | column -t jackson@192
go: finding github.com/dreamerjackson/minidiv latest
github.com/dreamerjackson/mathlib
github.com/dreamerjackson/minidiv v0.0.0-20200305104752-fcd15cf402bb
github.com/dreamerjackson/mydiv v1.0.2 [v1.0.3]
github.com/pkg/errors v0.9.1
如上所示,我們可以看到
github.com/dreamerjackson/mydiv
的當前版本爲v1.0.2
,但是最新的版本爲v1.0.3
更新直接和間接模塊
獲取直接和間接模塊可以使用
go get
指令。其中有不少的參數。下面命令以
最小版本原則
更新所有的直接和間接模塊
go get -t -d -v ./...
-t
考慮構建測試所需的模塊-d
下載每個模塊的源代碼-v
提供詳細輸出./…
在整個源代碼樹中執行這些操作,並且僅更新所需的依賴項注意,除非你瞭解項目的所有細節,否則慎用全部的最大最新版本的更新
如果go get中使用
-u
參數會用最大最新版本
原則更新所有的直接和間接模塊
~/mathlib » go get -u -t -d -v ./... jackson@192
go: finding github.com/dreamerjackson/minidiv latest
go: downloading github.com/dreamerjackson/mydiv v1.0.3
go: extracting github.com/dreamerjackson/mydiv v1.0.3
接着我們可以再次查看當前引用的版本,我們會發現模塊
github.com/dreamerjackson/mydiv
已經強制更新到了最新的v1.0.3
~/mathlib » go list -m all | grep mydiv jackson@192
github.com/dreamerjackson/mydiv v1.0.3
重置依賴關係
如果您不滿意所選的模塊和版本,則始終可以通過刪除
go.mod go.sum
模塊文件並再次運行go mod tidy來重置。當項目還不太成熟時這是一種選擇。
$ rm go.*
$ go mod init <module name>
$ go mod tidy
語義版本控制(semantic version)
Go模塊引入了一種新的導入路徑語法,即語義導入版本控制。每個語義版本均採用vMAJOR.MINOR.PATCH的形式。
MAJOR 主版本號,如果有大的版本更新,導致 API 和之前版本不兼容。我們遇到的就是這個問題。
MINOR 次版本號,當你做了向下兼容的新 feature。
PATCH 修訂版本號,當你做了向下兼容的修復 bug fix。
v 所有版本號都是 v 開頭。
如果兩個版本具有相同的主編號,則預期更高版本(如果您願意,更大版本)將與較早版本(較小版本)向後兼容。但是,如果兩個版本的主要編號不同,則它們之間沒有預期的兼容性關係。
因此我們在上面的實例中可以看到,go預料到v1.0.3與v1.0.1是兼容的,因爲他們有相同的主版本號
1
。但是一般我們將版本升級到了v2.0.0
,即被認爲是出現了重大的更新。
如上圖實例顯示了go對於版本更新的處理。
my/thing/v2
標識特定模塊的語義主版本2
。版本1是my/thing
,模塊路徑中沒有明確的版本。但是,當您引入主要版本2或更大版本時,必須在模塊名稱後添加版本,以區別於版本1和其他主要版本,因此版本2爲my/thing/v2,版本3爲my/thing/v3,依此類推。假設模塊A引入了模塊B和模塊C,模塊B引入了模塊Dv1.0.0,模塊C引入了模塊Dv2.0.0。則看起來就像是
A --> 模塊B --> 模塊D v1.0.0
A --> 模塊C --> 模塊D v2.0.0
由於v1 和v2 模塊的路徑不相同,因此他們之間會是互不干擾的兩個模塊。
下面我們用實例來驗證
首先我們給mydiv打一個v2.0.0的tag,其代碼如下,簡單修改了錯誤文字
v2.0.0 b can't = 0
package mydiv
import "github.com/pkg/errors"
func Div(a int,b int) (int,error){
if b==0{
return 0,errors.Errorf("v2.0.0 b can't = 0")
}
return a/b,nil
}
同時需要修改v2模塊路徑名爲:
module github.com/dreamerjackson/mydiv/v2
接着在mathlib中,代碼如下:
package main
import (
"fmt"
div "github.com/dreamerjackson/minidiv"
mydiv "github.com/dreamerjackson/mydiv/v2"
)
func main(){
_,err1:= mydiv.Div(4,0)
_,err2 := div.Div(4,0)
fmt.Println(err1,err2)
}
現在的依賴路徑可以表示爲爲:
mathlib --> 直接引用mydiv v2
mathlib --> 直接引用minidiv --> 間接引用mydiv v1
當我們運行代碼之後,會發現兩段代碼是共存的
v2.0.0 b can't = 0 ::v1.0.1 b can't = 0
接着執行go list
,模塊共存,驗證成功~
~/mathlib(master*) » go list -m all | grep mydiv
github.com/dreamerjackson/mydiv v1.0.1
github.com/dreamerjackson/mydiv/v2 v2.0.1
模塊鏡像(Module Mirror)
模塊鏡像於2019年八月推出,是go官方1.13版本的默認系統。模塊鏡像是一個代理服務器,以幫助加快構建本地應用程序所需的模塊的獲取。代理服務器實現了基於REST的API,並根據Go工具的需求進行了設計。
模塊鏡像將會緩存已請求的模塊及其特定版本,從而可以更快地檢索將來的請求。一旦代碼被獲取並緩存在模塊鏡像中,就可以將其快速提供給世界各地的用戶。
checksum數據庫
checksum數據庫也於2019八月推出,是可以用來防止模塊完整性、有效性的手段。它驗證特定版本的任何給定模塊代碼的正確性,而不管何人何時何地以及是如何獲取的。Google擁有唯一的校驗和數據庫,但是可以通過私有模塊鏡像對其進行緩存。
go module 環境變量
有幾個環境變量可以控制與模塊鏡像和checksum數據庫有關的行爲
GOPROXY:一組指向模塊鏡像的URL,用於獲取模塊。如果您希望Go工具僅直接從VCS地址獲取模塊,則可以將其設置爲
direct
。如果將此設置爲off
,則將不會下載模塊GOSUMDB:用於驗證給定模塊/版本的代碼的checksum數據庫地址。此地址用於形成一個適當的URL,該URL告訴Go工具在哪裏執行這些checksum校驗和查找。該URL可以指向Google擁有的checksum數據庫,也可以指向支持對checksum數據庫進行緩存或代理的本地模塊鏡像。如果您不希望Go工具驗證添加到go.sum文件中的給定模塊/版本的哈希碼,也可以將其設置爲off,僅在將任何新module添加到go.sum文件之時,才查詢checksum數據庫
GONOPROXY:一組基於URL的模塊路徑,這些模塊不會使用模塊鏡像來獲取,而應直接在VCS地址上獲取。
GONOSUMDB:一組基於URL的模塊路徑,這些模塊的哈希碼不會在checksum數據庫中查找。
GOPRIVATE:一個方便變量,用於將GONOPROXY和GONOSUMDB設置爲相同的默認值
我們可以通過go env
來查看到這些默認值
$ go env
GONOPROXY=""
GONOSUMDB=""
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOSUMDB="sum.golang.org"
這些默認值告訴Go工具使用Google模塊鏡像和Google checksum數據庫。如果這些Google服務可以訪問您所需的所有代碼,則建議使用此配置。如果Google模塊鏡像恰好以410(消失)或404(未找到)響應,則使用direct(這是GOPROXY配置的一部分)將允許Go工具更改路線並直接獲取模塊/版本VCS位置。
例如,如果我們需要訪問所有代理服務器,例如需要權限的位於
gitlab
等地的代碼,我們可以使用export GOPRIVATE=gitlab.XXX.com,gitlab.XXX-XX.com,XXX.io
多個域名用逗號分隔。
Athens搭建私有模塊鏡像
Athens是一個私有模塊鏡像,可以用於搭建私有模塊鏡像。使用私有模塊鏡像的一個原因是允許緩存公共模塊鏡像無法訪問的私有模塊。Athens項目提供了一個在Docker Hub
上發佈的Docker
容器,因此不需要特殊的安裝。
docker run -p '3000:3000' gomods/athens:latest
接下來,啓動一個新的終端會話以運行Athens,爲大家演示其用法。啓動Athens服務並通過額外的參數調試日誌(請確保系統已經安裝並啓動了docker)並有科學*上網的環境
$ docker run -p '3000:3000' -e ATHENS_LOG_LEVEL=debug -e GO_ENV=development gomods/athens:latest
INFO[7:11AM]: Exporter not specified. Traces won't be exported
2020-03-06 07:11:30.671249 I | Starting application at port :3000
接着我們修改GOPROXY參數,指向本地
3000
端口,初始化我們之前的項目,再次執行go mod tidy
$ export GOPROXY="http://localhost:3000,direct"
$ rm go.*
$ go mod init github.com/dreamerjackson/mathlib
$ go mod tidy
在Athens日誌中即可查看對應信息
INFO[7:39AM]: incoming request http-method=GET http-path=/github.com/dreamerjackson/mydiv/@v/list http-status=200
INFO[7:39AM]: incoming request http-method=GET http-path=/github.com/dreamerjackson/minidiv/@v/list http-status=200
INFO[7:39AM]: incoming request http-method=GET http-path=/github.com/dreamerjackson/minidiv/@latest http-status=200
詳細信息,查看參考資料中Athens的官方網站
go module 優勢
提供脫離
gopath
管理go代碼的優勢提供了代碼捆綁、版本控制、依賴管理的功能
供全球開發人員使用、構建,下載,授權、驗證,獲取,緩存和重用模塊(可以通過搭建自己的代理服務器來實現這些功能)
可以驗證模塊(對於任何給定的版本)始終包含完全相同的代碼,而不管它被構建了多少次,從何處獲取以及由誰獲取
總結
在本文中,使用詳細的實例講解了go module
是什麼,爲什麼需要,其最佳實踐以及其實現原理。希望讀者在這篇文章之後,能夠回答我們在開頭提出的問題
go module
是什麼?go module
爲什麼需要?go module
的基本使用方法是什麼?go module
如何管理版本與依賴?go module
如何解決依賴的衝突問題?go module
環境變量的配置與使用方式?如何搭建私有
module
鏡像?see you~
參考資料
項目鏈接
作者知乎
blog
Athens
talk youtube
Modules Part 03: Minimal Version Selection
How to Write Go Code
Go module 如何發佈 v2 及以上版本?
The Principles of Versioning in Go