Go Module是Go會在1.12中正式推出的包管理機(jī)制函筋。
Go mod 簡介
Golang一直存在一個被人詬病的問題是缺少一個官方的包依賴管理工具。從我個人的角度上來看存在兩個問題:
- GOPATH特性對于多工程的情況下积糯,支持不算友好。
- GOPATH無法對依賴包進(jìn)行有效的版本管理谦纱,沒有任何地方能夠表明依賴包的具體版本號看成,無法簡單清晰獲取到有效的依賴包版本信息等。
在Go1.11時,官方推出了go mod作為官方的依賴管理工具跨嘉。而go mod與之前的利用vendor特性的依賴管理工具的不同點(diǎn)在于川慌,go mod 更類似于maven這種本地緩存庫的管理方式,不論你有多少個工程,只要你引用的依賴的版本是一致的祠乃,那么在本地就只會有一份依賴文件的存在梦重。而vendor即使依賴的版本是相同的,但如果在不同的工程中進(jìn)行了引用亮瓷,也會在工程目錄下的vendor產(chǎn)生一份依賴文件琴拧。
所以Golang在1.11版本中引入了go mod機(jī)制,在統(tǒng)一的位置對依賴進(jìn)行管理嘱支。
go mod不同于以往基于GOPATH和Vendor的構(gòu)建方式蚓胸,其主要是通過GOPATH/pkg/mod下的緩存包來對工程進(jìn)行構(gòu)建挣饥。在Go 1.11中已經(jīng)可以使用,同以往新添加的功能一樣沛膳,go mod 可以通過GO111MODULE來控制是否啟用亮靴,GO111MODULE有一下三種類型。
- on 所有的構(gòu)建于置,都使用Module機(jī)制
- off 所有的構(gòu)建,都不使用Module機(jī)制贞岭,而是使用GOPATH和Vendor
- auto 在GOPATH下的工程八毯,不使用Module機(jī)制,不在GOPATH下的工程使用
Go mod化處理步驟
這里我主要說一下瞄桨,對舊工程如何進(jìn)行g(shù)o mod化處理话速。通過網(wǎng)上搜索的文檔加上自我實(shí)踐,我總結(jié)成了以下三個步驟芯侥。對于新工程的處理可直接從第二部分開始泊交。
- 將需要進(jìn)行版本管理的代碼從GOPATH路徑下移出
- 在項(xiàng)目的根目錄下使用命令go mod init projectName
- 在該目錄下執(zhí)行g(shù)o build main.go
從GOPATH中移出工程
這一步其實(shí)是不一定需要的,不過個人認(rèn)為可以將工程從GOPATH下移出柱查,單獨(dú)存放廓俭。只在GOPATH/pkg/mod目錄下只存放依賴文件。
在go1.12環(huán)境下唉工,我試驗(yàn)了一下環(huán)境變量GO111MODULE還是起作用的研乒。但是編譯時默認(rèn)為使用Module機(jī)制進(jìn)行編譯(即GO111MODULE=on)。
- 如果工程中存在go.mod文件淋硝,編譯時是從GOPATH/pkg/mod下查找依賴雹熬。
- 如果主動使用
export GO111MODULE=off
命令不使用Module機(jī)制,進(jìn)行編譯就會從GOPATH/src下查找依賴。會產(chǎn)生以下輸出谣膳。(編譯失敗是由于相應(yīng)目錄下無依賴文件)
/usr/local/Cellar/go/1.12.5/libexec/src/golang.org/x/tools/internal/tool (from $GOROOT)
/Users/dx/go/src/golang.org/x/tools/internal/tool (from $GOPATH)
初始化go mod
在這一步根據(jù)我的實(shí)踐竿报,需要說一下。一般網(wǎng)上的資料都是建議在工程的根目錄下執(zhí)行g(shù)o mod init projectName命令继谚。在執(zhí)行g(shù)o mod化之后烈菌,所有的引用都不再是以GOPATH為相對路徑的引用了,而是變成了以go.mod中初始化的項(xiàng)目名為起始的引用花履。
示例:
未使用go mod前僧界,當(dāng)前工程路徑和GOPATH為workspace/testmod
,即當(dāng)前工程的結(jié)構(gòu)如下:
├── bin
├── pkg
└── src
├── api
│ └── supply
│ └── location
│ └── location.go
└── main.go
location.go
package location
import (
"fmt"
)
func Hi(name string) string {
return fmt.Sprintf("hello %s",name)
}
在main.go中引用location.go時臭挽,import中路徑為"api/supply/location"捂襟。
package main
import(
"api/supply/location"
"fmt"
)
func main() {
fmt.Println(location.Hi("test"))
}
使用go mod后,當(dāng)前工程路徑為workspace/testmod
,在src目錄下執(zhí)行g(shù)o mod init testmod后,在main.go中引用location.go時,import中路徑需要變更為"testmod/api/supply/location",否則編譯時會報(bào)錯build command-line-arguments: cannot load api/supply/location: cannot find module providing package api/supply/location
go.mod
module testmod
go 1.12
main.go
package main
import(
"api/supply/location"
"fmt"
)
func main() {
fmt.Println(location.Hi("test"))
}
執(zhí)行g(shù)o build拉去依賴并編譯
這一步的主要作用是拉去代碼中的依賴文件欢峰。將會看到類似如下的輸出
$ go build main.go
go: finding github.com/mitchellh/go-wordwrap latest
go: finding github.com/maruel/panicparse/stack latest
go: finding github.com/nsf/termbox-go latest
再執(zhí)行完了之后葬荷,查看go.mod文件會發(fā)現(xiàn)產(chǎn)生了類似如下的內(nèi)容:
module github.com/test/testmod
require (
github.com/gizak/termui v2.2.0+incompatible
github.com/maruel/panicparse v1.1.1 // indirect
github.com/mattn/go-runewidth v0.0.3 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20180828145344-9e67c67572bc // indirect
github.com/nsf/termbox-go v0.0.0-20180819125858-b66b20ab708e // indirect
github.com/spf13/pflag v1.0.2 // indirect
)
至此涨共,對于一個項(xiàng)目的go mod化就已經(jīng)完成了。對于go.mod文件中的內(nèi)容會在下一節(jié)進(jìn)行敘述宠漩。
go mod命令簡介
這個子命令用來處理 go.mod
文件举反,上一小節(jié)我們已經(jīng)見過 go mod init
了。下面介紹下幾個用得到的命令扒吁。
go mod edit -fmt
格式化go.mod
文件火鼻。go mod edit -require=path@version
添加依賴或修改依賴版本,這里支持模糊匹配版本號雕崩,詳情可以看下文go get
的用法魁索。()-
go mod edit -replace=path1@version=path2@version
使用path2路徑的包來代替path1路徑的包。對于國內(nèi)用戶來說盼铁,手動維護(hù)這個文件是必然的粗蔚,因?yàn)槟阈枰?
golang.org/x/text
替換成github.com/golang/text
。示例go mod edit -replace=golang.org/x/sys@v0.0.0-20180830151530-49385e6e1522=github.com/golang/sys@v0.0.0-20180830151530-49385e6e1522
go mod tidy
從go.mod
刪除不需要的依賴饶火、新增需要的依賴鹏控,這個操作不會改變依賴版本。go mod download
命令會根據(jù)go.mod文件下載對應(yīng)的依賴項(xiàng)到GOPATH/pkg/mod路徑下肤寝。
go get 命令
在Go1.11后当辐,可以用此命令來獲取依賴的特定版本,可以用來升級和降級依賴鲤看。會自動修改 go.mod
文件瀑构,而且依賴的依賴版本號也可能會變。在 go.mod
中使用 exclude
排除的包刨摩,不能 go get
下來寺晌。
與以前不同的是,新版 go get
可以在末尾加 @
符號澡刹,用來指定版本呻征。go get 命令需在go.mod同級目錄下執(zhí)行,否則會報(bào)出錯誤go: cannot use path@version syntax in GOPATH mode
罢浇。而且在使用go get下載依賴時陆赋,要求倉庫必須用 vX.Y.Z
格式打 tag,以下是簡單羅列的匹配規(guī)則嚷闭。
go get github.com/gorilla/mux # 匹配最新的一個 tag
go get github.com/gorilla/mux@latest # 和上面一樣
go get github.com/gorilla/mux@v1.6.2 # 匹配 v1.6.2
go get github.com/gorilla/mux@e3702bed2 # 匹配 v1.6.2
go get github.com/gorilla/mux@c856192 # 匹配 c85619274f5d
go get github.com/gorilla/mux@master # 匹配 master 分支
latest 匹配最新的 tag攒岛。
v1.2.6 完整版本的寫法。
v1.2 匹配帶這個前綴的最新版本胞锰,如果最新版是 1.2.7灾锯,它們會匹配 1.2.7。
c856192 版本 hash 前綴嗅榕、分支名顺饮、無語義化的標(biāo)簽吵聪,在 go.mod 里都會會使用約定寫法 v0.0.0-20180517173623-c85619274f5d,也被稱作偽版本兼雄。
go get 可以模糊匹配版本號吟逝,但 go.mod 文件只體現(xiàn)完整的版本號,即 v1.2.0赦肋、v0.0.0-20180517173623-c85619274f5d块攒,只不過不需要手寫這么長的版本號,用 go get 或上文的 go mod edit -require 模糊匹配即可佃乘,它會把匹配到的完整版本號寫進(jìn) go.mod 文件囱井。(在執(zhí)行g(shù)o get 命令后,會自動修改go.mod文件)
go build 命令
-
go build -mod=readonly
防止隱式修改go.mod
恕稠,如果遇到有隱式修改的情況會報(bào)錯,可以用來測試go.mod
中的依賴是否整潔扶欣,但如果明確調(diào)用了go mod
鹅巍、go get
命令則依然會導(dǎo)致go.mod
文件被修改。 -
go build -mod=vendor
在開啟模塊支持的情況下料祠,用這個可以退回到使用 vendor 的時代骆捧。
使用本地包進(jìn)行開發(fā)測試
單獨(dú)把這個拿出來說一下的原因是,基于我們自己項(xiàng)目的一個需求髓绽,我們是把一些公共的配置與函數(shù)部分統(tǒng)一成了一個單獨(dú)的公共庫敛苇,但是在go mod的情況下,就會出現(xiàn)一個問題顺呕,每次對公共庫的修改測試都需要走提交更新枫攀、修改go.mod文件、更新本地依賴才可以進(jìn)行測試株茶,這樣明顯是及其不方便的来涨。所以通過查詢資料和實(shí)踐,發(fā)現(xiàn)可以通過使用replace使用本地包來進(jìn)行測試启盛。
使用本地包代提線上包進(jìn)行測試的方法,例如修改公共庫commons,在go mod中我可以增加一條這樣的替換
replace github.com/test/commons v1.1.1 => /Users/test/Workspace/bizgocommons
蹦掐,這樣就把使用的報(bào)的路徑指向了本地的包,省去了提交修改在下載的麻煩了僵闯。
注意:本地包在使用的時候不需要帶上版本信息卧抗。
go mod時遇到的問題
- 在工程中g(shù)o.etcd.io/etcd依賴時,在本地環(huán)境(mac)下可以成功編譯鳖粟,放到docker環(huán)境下(基礎(chǔ)鏡像為go1.12)的情況加會出現(xiàn)以下的錯誤信息社裆。
verifying go.etcd.io/etcd@v3.3.12+incompatible: checksum mismatch
downloaded: h1:V6PRYRGpU4k5EajJaaj/GL3hqIdzyPnBU8aPUp+35yw=
go.sum: h1:xR2YQOYo5JV5BMrUj9i1kcf2rEbpCQKHH2sKTtpAHiQ=
使用download中的值替換后,即可在docker下成功編譯向图。
通過實(shí)踐和上網(wǎng)查詢浦马,個人覺得原因是mac和docker環(huán)境下下載的etcd是有區(qū)別的时呀,可能含有某些操作系統(tǒng)相關(guān)的內(nèi)容。導(dǎo)致兩者的校驗(yàn)值不同晶默。