Go Modules使用教程


Go Modules 不完全教程

文章轉載自公眾號 Golang 成神之路 , 作者 L

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

 

0. 包管理的歷史

Golang 的包管理一直被大眾所詬病的一個點,但是我們可以看到現在確實是在往好的方向進行發展。下面是官方的包管理工具的發展歷史:

  • 在 1.5 版本之前,所有的依賴包都是存放在 GOPATH 下,沒有版本控制。這個類似 Google 使用單一倉庫來管理代碼的方式。這種方式的最大的弊端就是無法實現包的多版本控制,比如項目 A 和項目 B 依賴於不同版本的 package,如果 package 沒有做到完全的向前兼容,往往會導致一些問題。

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

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

  • 1.11 版本推出 modules 機制,簡稱 mod,也就是本文要討論的重點。modules 的原型其實是 vgo,關於 vgo,可以參考文章末尾的參考鏈接。

除此之外,社區也一直在有幾個活躍的包管理工具,使用廣泛且具有代表性的主要有下面幾個:

  • 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/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM