GO 依賴管理工具go Modules(官方推薦)

以前寫過一篇關於go管理依賴包工具 dep的文章,當時認爲dep將會成爲官方依賴工具,現在看來是自己圖樣圖斯內幕破了,正如官方一直提到dep是“official experiment”官方實驗項目的那樣,隨着go modules 在go1.11版推出,go1.12版功能不斷改進,再到go1.13版完善優化,正式扶正。預計dep將來也只能定格在“official experiment”了。

Go Modules有哪些特點:

  • Go Modules是官方正式推出的包依賴管理項目,由Russ Cox (即Go 現在的掌舵人)推動,dep是“official experiment”僅此而已。
  • Go modules 出現的目的之一就是爲了解決 GOPATH 的問題,也就相當於是拋棄 GOPATH 了。以前項目必須在$GOPATH/src 裏進行,現在Go 允許在 $GOPATH/src 外的任何目錄下使用 go.mod 創建項目。當然現在
  • 隨着模塊一起推出的還有模塊代理協議(Module proxy protocol),通過這個協議我們可以實現 Go 模塊代理(Go module proxy),也就是依賴鏡像。
  • Tag必須遵循語義化版本控制,如果沒有將忽略 Tag,然後根據你的 Commit 時間和哈希值再爲你生成一個假定的符合語義化版本控制的版本號。
  • Go modules 還默認認爲,只要你的主版本號不變,那這個模塊版本肯定就不包含 Breaking changes,因爲語義化版本控制就是這麼規定的啊。
  • Global Caching 這個主要是針對 Go modules 的全局緩存數據說明,如下:

    • 同一個模塊版本的數據只緩存一份,所有其他模塊共享使用。
    • 目前所有模塊版本數據均緩存在 $GOPATH/pkg/mod$GOPATH/pkg/sum 下,未來或將移至 $GOCACHE/mod $GOCACHE/sum 下( 可能會在當 $GOPATH 被淘汰後)。
    • 可以使用 go clean -modcache 清理所有已緩存的模塊版本數據。

另外在 Go1.11 之後 GOCACHE 已經不允許設置爲 off 了,我想着這也是爲了模塊數據緩存移動位置做準備,因此大家應該儘快做好適配。

如果你的版本是go1.12或更早版本,這裏建議升級到go1.13,來體驗一把go modules,看它能給你帶來哪些方面身心的愉悅。

本文將介紹使用Go Modules相關操作

1、安裝Go 1.13或升級到Go 1.13

安裝

2、配置環境變量

#修改 GOBIN 路徑(可選)
go env -w GOBIN=$HOME/bin
#打開 Go modules
go env -w GO111MODULE=on
#設置 GOPROXY
go env -w GOPROXY=https://goproxy.cn,direct

go env -w: Go1.13 新增了 go env -w 用於寫入環境變量,而寫入的地方是 os.UserConfigDir 所返回的路徑,需要注意的是 go env -w 不會覆寫。需要指出,它不會覆蓋系統環境變量。

GO111MODULE:

這個環境變量主要是 Go modules 的開關,主要有以下參數:

  • auto:只在項目包含了 go.mod 文件時啓用 Go modules,在 Go 1.13 中仍然是默認值,詳見
    :golang.org/issue/31857。
  • on:無腦啓用 Go modules,推薦設置,未來版本中的默認值,讓 GOPATH 從此成爲歷史。
  • off:禁用 Go modules。

GOPROXY:

這個環境變量主要是用於設置 Go 模塊代理,它的值是一個以英文逗號 “,” 分割的 Go module proxy 列表,默認是proxy.golang.org,國內訪問不了。這裏要感謝盛傲飛和七牛云爲中國乃至全世界的 Go 語言開發者提供免費、可靠的、持續在線的且經過CDN加速Go module proxy(goproxy.cn)。

其實值列表中的 “direct” 爲特殊指示符,用於指示 Go 回源到模塊版本的源地址去抓取(比如 GitHub 等),當值列表中上一個 Go module proxy 返回 404 或 410 錯誤時,Go 自動嘗試列表中的下一個,遇見 “direct” 時回源,遇見 EOF 時終止並拋出類似 “invalid version: unknown revision...” 的錯誤。

3、創建你的項目

這裏我們在 $GOPATH/src 外,創建 /var/www/demo實例

mkdir /var/www/demo
cd  /var/www/demo

新建main.go

package main

import (
    "github.com/gin-gonic/gin"
    "fmt"
)

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        fmt.Println("hello world!")
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

4、在/var/www/demo根目錄下

#生成go.mod文件
go mod init  demo

打開go.mod文件,內容

module demo

go 1.13

go.mod 是啓用了 Go moduels 的項目所必須的最重要的文件,它描述了當前項目(也就是當前模塊)的元信息,每一行都以一個動詞開頭,目前有以下 5 個動詞:

  • module:用於定義當前項目的模塊路徑。
  • go:用於設置預期的 Go 版本。
  • require:用於設置一個特定的模塊版本。
  • exclude:用於從使用中排除一個特定的模塊版本。
  • replace:用於將一個模塊版本替換爲另外一個模塊版本。

這裏的填寫格式基本爲包引用路徑+版本號,另外比較特殊的是 go $version,目前從 Go1.13 的代碼裏來看,還只是個標識作用,暫時未知未來是否有更大的作用。

5、在/var/www/demo根目錄下,執行 go build

go build

完成後項目

├── demo
├── go.mod
├── go.sum
└── main.go

項目中增加了go.sum、demo文件

go.sum文件內容

github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod 
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
...

go.sum類似於比如 dep 的 Gopkg.lock 的一類文件,它詳細羅列了當前項目直接或間接依賴的所有模塊版本,並寫明瞭那些模塊版本的 SHA-256 哈希值以備 Go在今後的操作中保證項目所依賴的那些模塊版本不會被篡改。

我們可以看到一個模塊路徑可能有如下兩種:

github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=

前者爲 Go modules 打包整個模塊包文件 zip 後再進行 hash 值,而後者爲針對 go.mod 的 hash 值。他們兩者,要不就是同時存在,要不就是隻存在 go.mod hash。

那什麼情況下會不存在 zip hash 呢,就是當 Go 認爲肯定用不到某個模塊版本的時候就會省略它的 zip hash,就會出現不存在 zip hash,只存在 go.mod hash 的情況。

go.mod文件內容發生了變化,增加了

require github.com/gin-gonic/gin v1.4.0

默認使用最新版本的package。

更換依賴版本

查看gin所有歷史版本

go list -m -versions github.com/gin-gonic/gin

github.com/gin-gonic/gin v1.1.1 v1.1.2 v1.1.3 v1.1.4 v1.3.0 v1.4.0

如果想更換依賴版本,比如v1.3.0,怎麼辦?

只需執行如下命令

go mod edit -require="github.com/gin-gonic/[email protected]"
go tidy #更新現有依賴

@後跟版本號,這個時候go.mod已經修改好了

require github.com/gin-gonic/gin v1.3.0

查看所有項目依賴的包

go list -m all
github.com/davecgh/go-spew v1.1.0
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3
github.com/gin-gonic/gin v1.4.0
github.com/golang/protobuf v1.3.1
github.com/json-iterator/go v1.1.6
github.com/mattn/go-isatty v0.0.7
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
github.com/modern-go/reflect2 v1.0.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/objx v0.1.0
github.com/stretchr/testify v1.3.0
github.com/ugorji/go v1.1.4
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
golang.org/x/text v0.3.0
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
gopkg.in/go-playground/assert.v1 v1.2.1
gopkg.in/go-playground/validator.v8 v8.18.2
gopkg.in/yaml.v2 v2.2.2

注意

使用go.mod管理依賴會對go get命令產生一定影響,

  • go help module-getgo help gopath-get分別去了解 Go modules 啓用和未啓用兩種狀態下的 go get 的行爲
  • go get 拉取新的依賴

    • 拉取最新的版本(優先擇取 tag):go get golang.org/x/text@latest
    • 拉取 master 分支的最新 commit:go get golang.org/x/text@master
    • 拉取 tag 爲 v0.3.2 的 commit:go get golang.org/x/[email protected]
    • 拉取 hash 爲 342b231 的 commit,最終會被轉換爲 v0.3.2:go get golang.org/x/text@342b2e
    • go get -u 更新現有的依賴

go mod 相關命令

go mod download 下載 go.mod 文件中指明的所有依賴

go mod tidy 整理現有的依賴,刪除未使用的依賴。

go mod graph 查看現有的依賴結構

go mod init 生成 go.mod 文件 (Go 1.13 中唯一一個可以生成 go.mod 文件的子命令)

go mod edit 編輯 go.mod 文件

go mod vendor 導出現有的所有依賴 (事實上 Go modules 正在淡化 Vendor 的概念)

go mod verify 校驗一個模塊是否被篡改過

go clean -modcache 清理所有已緩存的模塊版本數據。

go mod 查看所有 go mod的使用命令。

參考資料:

https://segmentfault.com/a/11...

https://learnku.com/articles/...

links

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章