https://www.jianshu.com/p/c666ebdb462b
Go mod 簡介
Golang一直存在一個被人詬病的問題是缺少一個官方的包依賴管理工具。從我個人的角度上來看存在兩個問題:
- GOPATH特性對於多工程的情況下,支持不算友好。
- GOPATH無法對依賴包進行有效的版本管理,沒有任何地方能夠表明依賴包的具體版本號,無法簡單清晰獲取到有效的依賴包版本信息等。
在Go1.11時,官方推出了go mod作為官方的依賴管理工具。而go mod與之前的利用vendor特性的依賴管理工具的不同點在於,go mod 更類似於maven這種本地緩存庫的管理方式,不論你有多少個工程,只要你引用的依賴的版本是一致的,那么在本地就只會有一份依賴文件的存在。而vendor即使依賴的版本是相同的,但如果在不同的工程中進行了引用,也會在工程目錄下的vendor產生一份依賴文件。
所以Golang在1.11版本中引入了go mod機制,在統一的位置對依賴進行管理。
go mod不同於以往基於GOPATH和Vendor的構建方式,其主要是通過GOPATH/pkg/mod下的緩存包來對工程進行構建。在Go 1.11中已經可以使用,同以往新添加的功能一樣,go mod 可以通過GO111MODULE來控制是否啟用,GO111MODULE有一下三種類型。
- on 所有的構建,都使用Module機制
- off 所有的構建,都不使用Module機制,而是使用GOPATH和Vendor
- auto 在GOPATH下的工程,不使用Module機制,不在GOPATH下的工程使用
Go mod化處理步驟
這里我主要說一下,對舊工程如何進行go mod化處理。通過網上搜索的文檔加上自我實踐,我總結成了以下三個步驟。對於新工程的處理可直接從第二部分開始。
- 將需要進行版本管理的代碼從GOPATH路徑下移出
- 在項目的根目錄下使用命令go mod init projectName
- 在該目錄下執行go build main.go
從GOPATH中移出工程
這一步其實是不一定需要的,不過個人認為可以將工程從GOPATH下移出,單獨存放。只在GOPATH/pkg/mod目錄下只存放依賴文件。
在go1.12環境下,我試驗了一下環境變量GO111MODULE還是起作用的。但是編譯時默認為使用Module機制進行編譯(即GO111MODULE=on)。
- 如果工程中存在go.mod文件,編譯時是從GOPATH/pkg/mod下查找依賴。
- 如果主動使用
export GO111MODULE=off
命令不使用Module機制,進行編譯就會從GOPATH/src下查找依賴。會產生以下輸出。(編譯失敗是由於相應目錄下無依賴文件)
go mod命令簡介
這個子命令用來處理 go.mod
文件,上一小節我們已經見過 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路徑的包。對於國內用戶來說,手動維護這個文件是必然的,因為你需要把
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
命令會根據go.mod文件下載對應的依賴項到GOPATH/pkg/mod路徑下。
go get 命令
在Go1.11后,可以用此命令來獲取依賴的特定版本,可以用來升級和降級依賴。會自動修改 go.mod
文件,而且依賴的依賴版本號也可能會變。在 go.mod
中使用 exclude
排除的包,不能 go get
下來。
與以前不同的是,新版 go get
可以在末尾加 @
符號,用來指定版本。go get 命令需在go.mod同級目錄下執行,否則會報出錯誤go: cannot use path@version syntax in GOPATH mode
。而且在使用go get下載依賴時,要求倉庫必須用 vX.Y.Z
格式打 tag,以下是簡單羅列的匹配規則。
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 分支
go build 命令
go build -mod=readonly
防止隱式修改go.mod
,如果遇到有隱式修改的情況會報錯,可以用來測試go.mod
中的依賴是否整潔,但如果明確調用了go mod
、go get
命令則依然會導致go.mod
文件被修改。go build -mod=vendor
在開啟模塊支持的情況下,用這個可以退回到使用 vendor 的時代
使用本地包進行開發測試
單獨把這個拿出來說一下的原因是,基於我們自己項目的一個需求,我們是把一些公共的配置與函數部分統一成了一個單獨的公共庫,但是在go mod的情況下,就會出現一個問題,每次對公共庫的修改測試都需要走提交更新、修改go.mod文件、更新本地依賴才可以進行測試,這樣明顯是及其不方便的。所以通過查詢資料和實踐,發現可以通過使用replace使用本地包來進行測試。
使用本地包代提線上包進行測試的方法,例如修改公共庫commons,在go mod中我可以增加一條這樣的替換
replace github.com/test/commons v1.1.1 => /Users/test/Workspace/bizgocommons
,這樣就把使用的報的路徑指向了本地的包,省去了提交修改在下載的麻煩了。
注意:本地包在使用的時候不需要帶上版本信息。
go mod時遇到的問題
- 在工程中go.etcd.io/etcd依賴時,在本地環境(mac)下可以成功編譯,放到docker環境下(基礎鏡像為go1.12)的情況加會出現以下的錯誤信息。
verifying go.etcd.io/etcd@v3.3.12+incompatible: checksum mismatch
downloaded: h1:V6PRYRGpU4k5EajJaaj/GL3hqIdzyPnBU8aPUp+35yw=
go.sum: h1:xR2YQOYo5JV5BMrUj9i1kcf2rEbpCQKHH2sKTtpAHiQ=
使用download中的值替換后,即可在docker下成功編譯。
通過實踐和上網查詢,個人覺得原因是mac和docker環境下下載的etcd是有區別的,可能含有某些操作系統相關的內容。導致兩者的校驗值不同。