Go Modules使用教程

Go Modules 不徹底教程

文章轉載自公衆號 Golang 成神之路 , 做者 L

Go Modules 是 Golang 官方最近幾個版本推出的原生的包管理方式,在此以前,社區也不乏多種包管理方案。在討論 Go Modules 以前,咱們先回顧一下 Golang 的包管理歷史的發展。而後討論一下 Go Modules 的使用以及一些特性,篇幅有限,有些地方不方便展開,後面有時間再深刻。行文倉促,不當之處,多多指教。javascript

 

0. 包管理的歷史java

Golang 的包管理一直被大衆所詬病的一個點,可是咱們能夠看到如今確實是在往好的方向進行發展。下面是官方的包管理工具的發展歷史:nginx

  • 在 1.5 版本以前,全部的依賴包都是存放在 GOPATH 下,沒有版本控制。這個相似 Google 使用單一倉庫來管理代碼的方式。這種方式的最大的弊端就是沒法實現包的多版本控制,好比項目 A 和項目 B 依賴於不一樣版本的 package,若是 package 沒有作到徹底的向前兼容,每每會致使一些問題。git

  • 1.5 版本推出了 vendor 機制。所謂 vendor 機制,就是每一個項目的根目錄下能夠有一個 vendor 目錄,裏面存放了該項目的依賴的 package。go build 的時候會先去 vendor 目錄查找依賴,若是沒有找到會再去 GOPATH 目錄下查找。github

  • 1.9 版本推出了實驗性質的包管理工具 dep,這裏把 dep 歸結爲 Golang 官方的包管理方式可能有一些不太準確。關於 dep 的爭議頗多,好比爲何官方後來沒有直接使用 dep 而是弄了一個新的 modules,具體細節這裏不太方便展開。golang

  • 1.11 版本推出 modules 機制,簡稱 mod,也就是本文要討論的重點。modules 的原型實際上是 vgo,關於 vgo,能夠參考文章末尾的參考連接。sql

除此以外,社區也一直在有幾個活躍的包管理工具,使用普遍且具備表明性的主要有下面幾個:swift

  • godep緩存

  • glide安全

  • govendor

關於這幾種包管理工具的使用這裏就再也不詳述了。

 

1. modules 簡單使用方式


下面看一下 modules 的簡單使用方式。

1.1 準備工做

Golang 版本:1.12.3。在 1.12 版本以前,使用 Go modules 以前須要環境變量 GO111MODULE:

  • GO111MODULE=off: 不使用 modules 功能。

  • GO111MODULE=on: 使用 modules 功能,不會去 GOPATH 下面查找依賴包。

  • GO111MODULE=auto: Golang 本身檢測是否是使用 modules 功能。

在 GOPATH 以外建立一個項目 mod-demo,包含一個 main.go 文件,內容以下:

packagemain
import("github.com/astaxie/beego")
funcmain() { beego.Run()}

1.2 初始化

初始化很簡單,在項目根目錄執行命令 go mod init mod-demo ,而後會生成一個 go.mod 文件以下。

➜ mod-demo $ gomod init mod-demogo: creating new go.mod: module .➜ mod-demo $ lsgo.mod main.go➜ mod-demo $ catgo.modmodule .
go 1.12

 

這裏比較關鍵的就是這個 go.mod 文件,這個文件中標識了咱們的項目的依賴的 package 的版本。執行 init 暫時尚未將全部的依賴管理起來。咱們須要將程序 run 起來(好比執行 go run/test),或者 build(執行命令 go build)的時候,纔會觸發依賴的解析。

好比使用 go run 便可觸發 modules 工做。

➜ mod-demo $ gorun main.gogo: extracting github.com/astaxie/beego v1.12.02019/09/08 23:23:03.507 [I] http server Running on http://:8080

這個時候咱們再查看 go.mod 文件:

➜ mod-demo $ catgo.modmodule mod-demo
go 1.12
require (github.com/astaxie/beego v1.12.0github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect)

同時咱們發現項目目錄下多了一個 go.sum 用來記錄每一個 package 的版本和哈希值。go.mod 文件正常狀況會包含 module 和 require 模塊,除此以外還能夠包含 replace 和 exclude 模塊。

這些 package 並非直接存儲到 $GOPATH/src,而是存儲到 $GOPATH/pkg/mod 下面,不一樣版本並存的方式。

➜ mod-demo $ ls$GOPATH/pkg/mod/github.com/astaxiebeego@v1.11.0 beego@v1.11.1 beego@v1.12.0

1.3 依賴升級(降級)

可使用以下命令來查看當前項目依賴的全部的包。

➜ mod-demo $ golist -m-uallgo: finding github.com/beego/x2j latestgo: finding github.com/cloudflare/golz4 latestgo: finding github.com/siddontang/go latestgo: finding github.com/shiena/ansicolor latestgo: finding github.com/couchbase/go-couchbase latestgo: finding github.com/siddontang/rdb latestgo: finding gopkg.in/check.v1 latestgo: finding github.com/siddontang/ledisdb latestgo: finding github.com/ssdb/gossdb latestgo: finding github.com/couchbase/gomemcached latestgo: finding github.com/wendal/errors latestgo: finding github.com/couchbase/goutils latestgo: finding golang.org/x/net latestgo: finding github.com/cupcake/rdb latestgo: finding github.com/beego/goyaml2 latestgo: finding golang.org/x/crypto latestgo: finding github.com/bradfitz/gomemcache latestgithub.com/Knetic/govaluate v3.0.0+incompatiblegithub.com/OwnLocal/goes v1.0.0github.com/astaxie/beego v1.12.0...

若是我想要升級(降級)某個 package 則只須要 go get 便可,好比:

go get package@version

 

須要注意的是,在 modules 模式開啓和關閉的狀況下,go get 的使用方式不是徹底相同的。在 modules 模式開啓的狀況下,能夠經過在 package 後面添加 @version 來代表要升級(降級)到某個版本。若是沒有指明 version 的狀況下,則默認先下載打了 tag 的 release 版本,好比 v0.4.5 或者 v1.2.3;若是沒有 release 版本,則下載最新的 pre release 版本,好比 v0.0.1-pre1。若是尚未則下載最新的 commit。這個地方給咱們的一個啓示是若是咱們不按規範的方式來命名咱們的 package 的 tag,則 modules 是沒法管理的。version 的格式爲 v(major).(minor).(patch) ,更多信息能夠參考:https://semver.org/ 。

好比咱們如今想將咱們依賴中的 beego 項目的版本改成 v1.11.1,則能夠像以下操做。咱們發現執行完 go get 以後, go.mod 中的項目的版本也相應改變了。

➜ mod-demo $ gogetgithub.com/astaxie/beego@v1.11.1go: finding github.com/astaxie/beego v1.11.1go: downloading github.com/astaxie/beego v1.11.1go: extracting github.com/astaxie/beego v1.11.1➜ mod-demo $ catgo.modmodule .
go 1.12
require (github.com/OwnLocal/goes v1.0.0 // indirectgithub.com/astaxie/beego v1.11.1 // indirectgithub.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect)

在 modules 開啓的模式下,go get 還支持 version 模糊查詢,好比 > v1.0.0 表示大於 v1.0.0 的可以使用版本;< v1.12.0 表示小於 v1.12.0  版本下最近可用的版本。version 的比較規則按照 version 的各個字段來展開。

除了指定版本,咱們還可使用以下命名使用最近的可行的版本:

  • go get -u 使用最新的 minor 或者 patch 版本

  • go get -u=patch 使用最新的 patch 版本

1.4 vendor

咱們知道 Go 1.5 推出了 vendor 機制,go mod 也能夠支持 vendor 機制,將依賴包拷貝到 vendor 目錄。可是像一些 test case 裏面的依賴包並不會拷貝的 vendor 目錄中。

➜ mod-demo $ gohelp mod vendorusage: go mod vendor [-v]
Vendor resets the main module's vendor directory to include all packagesneeded to build and test all the main module's packages.It does not include test code forvendored packages.

 

2. modules 特性


上面介紹了 go modules 的簡單使用方法,可是 modules 的一些更高級的特性沒有介紹,將在下面進行展開。

2.1 GoProxy

proxy 顧名思義,代理服務器。衆所周知,有些 Golang 的 package 在國內是沒法直接 go get 的。在以前,咱們解決這個問題,通常都是經過設置 http_proxy/https_proxy 來解決。GoProxy 至關於官方提供了一種 proxy 的方式讓用戶來進行包下載。要使用 GoProxy 只須要設置環境變量 GOPROXY 便可。目前公開的 GOPROXY 有:

  • goproxy.io

  • goproxy.cn: 由七牛雲提供,參考 github repo

固然你也能夠實現本身的 GoProxy 服務,好比項目中的依賴包含外部依賴和內部依賴的時候,那麼只須要實現 module proxy protocal 協議便可。

值得注意的是,在最新 release 的 Go 1.13 版本中默認將 GOPROXY 設置爲 https://proxy.golang.org,這個對於國內的開發者是沒法直接使用的。因此若是升級了 Go 1.13 版本必定要把 GOPROXY 手動改掉。

2.2 Replace

replace 主要爲了解決某些包發生更名的問題。

對於另一種場景有的時候也是有用的,好比對於有些 golang.org/x/ 下面的包因爲某些緣由在國內是下載不了的,可是對應的包在 github 上面是有一份拷貝的,這個時候咱們就能夠將 go.mod 中的包進行 replace 操做。

下面是一個 Beego 項目的 go.mod 的 replace 的示例。

replace golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 => github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85
replace gopkg.in/yaml.v2 v2.2.1 => github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d

2.3 SubCommand

modules 支持的 subcommand 以下。

mod-demo $ gohelp modGo mod provides access to operations on modules.
Note that support formodules is built into all the go commands,not just 'go mod'. For example, day-to-day adding, removing, upgrading,and downgrading of dependencies should be doneusing 'go get'.See 'go help modules'foran overview of module functionality.
Usage:
go mod <command> [arguments]
The commands are:
download download modules to local cacheedit edit go.mod from tools or scriptsgraph print module requirement graphinit initialize new module incurrent directorytidy add missing and remove unused modulesvendor makevendored copy of dependenciesverify verify dependencies have expected contentwhy explain why packages or modules are needed
Use "go help mod <command>"formore information about a command.

每一個 subcommand 的含義以下:

  • download: 下載 modules 到本地緩存

  • edit: 提供一種命令行交互修改 go.mod 的方式

  • graph: 將 module 的依賴圖在命令行打印出來,其實並非很直觀

  • init: 初始化 modules,會生成一個 go.mod 文件

  • tidy: 清理 go.mod 中的依賴,會添加缺失的依賴,同時移除沒有用到的依賴

  • vendor: 將依賴包打包拷貝到項目的 vendor 目錄下,值得注意的是並不會將 test code 中的依賴包打包到 vendor 中。這種設計在社區也引發過幾回爭論,可是並無達成一致。

  • verify: verify 用來檢測依賴包自下載以後是否被改動過。

  • why: 解釋爲何 package 或者 module 是須要,可是看上去解釋的理由並非很是的直觀。

➜ mod-demo $ gomod why github.com/astaxie/beego# github.com/astaxie/beegomod-demogithub.com/astaxie/beego

 

3. Go 1.13 對 modules 的改動


上面在討論 GoProxy 的時候提到了 Go 1.13 默認設置環境變量 GOPROXY 的值,除此以外 Go 1.13 對 modules 還有哪些值得注意的改動呢?

3.1 默認開啓

modules 在 Go 1.13 的版本下是默認開啓的。

3.2 GOPRIVATE

前面也說到對於一些內部的 package,GoProxy 並不能很好的處理,Go 1.13 推出了 GOPRIVATE 機制。只須要設置這個環境變量,而後標識出哪些 package 是 private 的,那麼對於這個 package 的處理將不會從 proxy 下載。GOPRIVATE 的值是一個以逗號分隔的列表,支持正則(正則語法遵照 Golang 的 包 path.Match)。下面是一個 GOPRIVATE 的示例:

GOPRIVATE=*.corp.example.com,rsc.io/private

上面的 GOPRIVATE 表示以 *.corp.example.com 或者 rsc.io/private 開頭的 package 都是私有的。

3.3 GOSUMDB

GOSUMDB 的全稱爲 Go CheckSum Database,用來下載的包的安全性校驗問題。包的安全性在使用 GoProxy 以後更容易出現,好比咱們引用了一個不安全的 GoProxy 以後而後下載了一個不安全的包,這個時候就出現了安全性問題。對於這種狀況,能夠經過 GOSUMDB 來對包的哈希值進行校驗。固然若是想要關閉哈希校驗,能夠將 GOSUMDB 設置爲 off;若是要對部分包關閉哈希校驗,則能夠將包的前綴設置到環境變量中 GONOSUMDB 中,設置規則相似 GOPRIVATE。

關於 GOSUMDB 的配置格式爲:<db_name>+<publickey>+<url>。

GOSUMDB="sum.golang.org"GOSUMDB="sum.golang.org+<publickey>"GOSUMDB="sum.golang.org+<publickey> https://sum.golang.org"

上面三種配置都是合理的,由於對於 sum.golang.org,Go 本身知道其對應的 publickey 和 url,因此咱們只要配置一個名字便可,對於另一個 sum.golang.google.cn 也是同樣。除此以外的,都須要指明 publickey,url 默認是 https://<db_name>。

關於 GOSUMDB 更多的詳細信息能夠參考:https://golang.org/cmd/go/#hdr-Module_authentication_failures

 

4. 總結


Golang 包管理歷經多個版本,目前來看並無一個徹底讓開發者滿意的方案,好比相似 Java 的 maven 的包管理方式。不少開發者也表示 modules 的管理方式也不是很直觀。其實 Golang 的 package 使用 git 的管理方式來管理實際上是一個很好的管理方式,咱們最須要的其實如何能讓普通開發者獲取到任何 package 的心智負擔降到最低。值得欣慰的是咱們確實有看到你們在這個方向上的努力,好比 modules 的 GoProxy。

放眼將來,但願會有一種令大部分普通開發者滿意的包管理方式吧。

 

5. Reference


  1. https://research.swtch.com/vgo

  2. https://github.com/golang/go/wiki/Modules

  3. https://roberto.selbach.ca/intro-to-go-modules/

  4. https://roberto.selbach.ca/playing-with-go-modules/

  5. https://semver.org/

  6. https://golang.org/cmd/go/#hdr-Module_authentication_failures

  7. https://golang.org/doc/go1.13#modules

  8. https://goproxy.io

  9. https://github.com/goproxy/goproxy.cn

  10. https://codeengineered.com/blog/2018/golang-godep-to-vgo/

相關文章
相關標籤/搜索