手把手教你如何創(chuàng)建及使用Go module
Go module是從Go 1.11版本才引入的新功能。其目標是取代舊的的基于GOPATH方法來指定在工程中使用哪些源文件或?qū)氚?。本文首先分析Go引入module之前管理依賴的優(yōu)缺點,然后針對這些缺點,看module是如何解決的。一、傳統(tǒng)的包管理方式-package在Go1.11之前,如果想要編寫Go代碼以及引入第三方包,則需要將源代碼寫在GOPATH/src目錄下。即開發(fā)者只能將研發(fā)的項目放到GOPATH目錄下。同時,將引入的第三方包會下載到GOPATH/pkg目錄下。我們先來看下在這種包管理模式下,使用go get是如何安裝依賴包的,然后再分析這種包管理的不足。
第一步,使用import引入模塊下具體的包。因為在encodex的module中,我們設(shè)置的引入路徑是github.com/goxuetang/encodex, 即go.mod文件的第一行。hash包是encodex模塊下的一個包。所以我們引入的完整路徑是:
好,我們現(xiàn)在來看另外一個問題,下載下來的包存在哪里了。2.5 module存儲在哪里當go get將包下載下來后,會將其存儲到GOPATH/pkg/mod目錄下。通過go env可以查看GOPATH環(huán)境變量的具體指向目錄,我的環(huán)境下的GOPATH=/Users/YuYang/go,如下是上節(jié)中引入的encodex模塊。如下圖所示:
現(xiàn)在我們在main模塊中引入github.com/go-redis/redis 模塊,然后查看go.mod文件,發(fā)現(xiàn)有如下間接的依賴模塊,這里的模塊正是在github.com/go-redis/redis 中引入的模塊,可以查看github.com/go-redis/redis 模塊的go.mod文件以確認。
那么,在工程項目模塊(PROJECT MODULE)中需要間接依賴MODULE 1的哪個版本呢?如果我們使用v1.0.1,那么MODULE B有可能會產(chǎn)生異常。在語義化版本中,我們知道小版本或補丁版本應(yīng)該向后兼容,即v1.0.2是兼容v1.0.1的,所以在PROJECZT MODULE中應(yīng)該選擇MODULE 1的v1.0.2版本。
想要了解更多 Golang 相關(guān)的內(nèi)容,歡迎掃描下方???關(guān)注?公眾號,回復(fù)關(guān)鍵詞 [實戰(zhàn)群]? ,就有機會進群和我們進行交流~
1.1 go get的工作流程
我們以在項目中引入github.com/go-redis/redis包為例。在項目中使用import導(dǎo)入該包:import "github.com/go-redis/redis"
然后我們需要使用go get命令將該包下載下來:go get github.com/go-redis/redis
運行g(shù)o get命令后,Go會訪問 https://github.com/go-redis/redis 并下載該包。一旦下載完成,該包就會被保存到?$GOPATH/pkg/github.com/go-redis/redis?目錄下。那么從執(zhí)行g(shù)o get命令到包被保存到對應(yīng)的目錄期間,go get都經(jīng)歷了哪些過程呢?首先,Go會將包拼接成https協(xié)議的URL地址。這里是 https://github.com/go-redis/redis 。Go的第三方包是存儲在像GIT或SVN這樣的在線版本控制管理系統(tǒng)上的。Go目前支持的在線版本管理類型如下:Bazaar .bzr
Fossil .fossil
Git .git
Mercurial .hg
Subversion .svn
所以,在示例中,Go首先會解析github.com/go-redis/redis.git (模板格式:github.com/go-redis/redis{.type})。其次,根據(jù)支持的協(xié)議依次嘗試clone該包。若該在線版本管理系統(tǒng)支持多種協(xié)議,那么Go會依次嘗試。例如,Git支持 https:// 和 git+ssh:// 協(xié)議 , 那么Go會依次使用對應(yīng)的協(xié)議進行解析該包。如果Go成功解析了對應(yīng)的URL地址,那么該包將會被clone并保存到$GOPATH/pkg目錄下。最后,若版本管理系統(tǒng)不是Go所支持的,則嘗試查找META信息。在這種場景下,Go也會試圖使用https或http協(xié)議拼裝成的URL地址去解析。并從返回的HTML代碼中查找META信息:"go-import" content="import-prefix type repo-root">
- import-prefix:?這是模塊所導(dǎo)入的路徑。在我們的示例中是github.com/go-redis/go
- type:在線版本管理系統(tǒng)的類型??梢允巧厦嫖覀兲岬降腉o支持的類型之一。在我們的示例中是git。
- repo-root:?代碼倉庫在版本控制系統(tǒng)中的根URL地址。例如,在我們的示例中,應(yīng)該是 https://github.com/go-redis/redis.git。

二、現(xiàn)代包管理方式-module
2.1 什么是module
一個module就是一個包含多個package的目錄,即一個package的集合。?其要實現(xiàn)的目標如下:- 首先,研發(fā)者應(yīng)該能夠在任何目錄下工作,而不僅僅是在GOPATH指定的目錄。
- 可以安裝依賴包的指定版本,而不是只能從master分支安裝最新的版本。
- 可以導(dǎo)入同一個依賴包的多個版本。當我們老項目使用老版本,新項目使用新版本時會非常有用。
- 要有一個能夠羅列當前項目所依賴包的列表。這個的好處是當我們發(fā)布項目時不用同時發(fā)布所依賴的包。Go能夠根據(jù)該文件自動下載對應(yīng)的包。
- 一個module必須是一個代碼控制系統(tǒng)的倉庫,并且一個倉庫應(yīng)該只能包含一個module。
- 一個module應(yīng)該包含一個或多個package。
- 一個包應(yīng)該在同一個目錄下包含一個或多個go文件
2.2 如何創(chuàng)建module
第一,我們在GOPATH之外的任何位置創(chuàng)建一個目錄。這里我們使用encodex,該encodex包含一些對字符串的編碼功能函數(shù),例如md5,sha1等。如下圖:

第二,在本地的目錄下執(zhí)行g(shù)o mod init 命令來初始化Go module。
go mod init github.com/goxuetang/encodex
該命令會在encodex的根目錄下創(chuàng)建go.mod文件,go.mod文件會包含我們定義的module的導(dǎo)入路徑和依賴的包及對應(yīng)的版本。如下所示:
git init
git remote add origin https://github.com/goxuetang/encodex.git
第三,我們在encodex的hash包中添加如下代碼:

vMajor.Minor.Patch
該格式以固定的字母 v 開頭,Major代表主版本,Minor代表次版本,Patch代表不定版本。只有在版本不兼容之前的版本時,才會改動主版本Major。當做了向下兼容的功能時會改動Minor。當對次版本Minor做了問題修正時會改動Patch。詳細的語義化版本可參考語義化版本官方文檔進一步閱讀。Go語言指出,當一個module的新老版本不兼容時,新版本應(yīng)該發(fā)布一個新的主版本。同時,Go會認為這是一個獨立的module,和之前的老版本沒有任何關(guān)系。Git的分支本質(zhì)上是一個歷史提交的記錄。對于每一次提交都有一個唯一的標識對應(yīng)。對于每一個唯一標識,我們還可以給一個語義化的版本別名,也就是我們所說的tag。最后,我們可以給我們的module打一個tag了。因為是第一個版本,所以我們使用版本v1.0.0,如下:
git tag v1.0.0
git push --tags
到此,我們的module已經(jīng)發(fā)布了,并由一個v1.0.0的tag版本。接下來,我們看看在項目中如何使用該module2.4 如何使用第三方module
我們在新建的main module中創(chuàng)建了一個main.go文件,在該module下要想使用encodex模塊下的包,則需要引入和安裝兩個步驟。在文件中使用import語句引入包,如下圖:?
第一步,使用import引入模塊下具體的包。因為在encodex的module中,我們設(shè)置的引入路徑是github.com/goxuetang/encodex, 即go.mod文件的第一行。hash包是encodex模塊下的一個包。所以我們引入的完整路徑是:import "github.com/goxuetang/encodex/hash"
第二步,使用go get命令安裝引入的包。使用go get命令時,可以指定包的具體版本,如下:go get github.com/goxuetang/encodex/hash@v1.0.0
也可以不指定版本,這時go get命令會自動的查找最近的版本,如下:go get github.com/goxuetang/encodex/hash
go get:added github.com/goxuetang/encodex v1.0.0
如圖所示:

好,我們現(xiàn)在來看另外一個問題,下載下來的包存在哪里了。2.5 module存儲在哪里當go get將包下載下來后,會將其存儲到GOPATH/pkg/mod目錄下。通過go env可以查看GOPATH環(huán)境變量的具體指向目錄,我的環(huán)境下的GOPATH=/Users/YuYang/go,如下是上節(jié)中引入的encodex模塊。如下圖所示:
go get -u=patch
如果想更新同一個大版本下的小版本,那么可以使用如下命令:
go get -u
該命令是如果小版本有更新,則升級小版本。如果只有補丁版本有更新,則會升級補丁版本。如果想升級到指定的版本,則使用指定版本的命令:go get module@version
例如,要將encodex模塊升級到v1.1.3版本,則使用如下命令:
go get github.com/goxuetang/encodex@v1.1.3
3.2 如何升級module的大版本
如果想要升級大版本則需要重新安裝大版本,因為在上面我們有提到,在Go中,會將一個大版本視為一個全新的模塊。因此,需要使用go get安裝該大版本的模塊,同時在對應(yīng)的文件中通過import引入該包。例如encodex模塊升級到了v2版本,那么就需要在encodex模塊的go.mod中將導(dǎo)入路徑更改為v2。如下:github.com/goxuetang/encodex/v2
然后就可以在工程中引用該v2版本的模塊了。如下:
import newHash github.com/goxuetang/encodex/v2/hash
同時使用go get命令下載并安裝該模塊:
go get github.com/goxuetang/encodex/v2
四、間接依賴
一個工程所依賴的模塊可分為直接依賴和**間接依賴。**直接依賴就是我們的工程文件中使用import語句導(dǎo)入的模塊。而間接依賴就是我們直接依賴的模塊所依賴的。如下圖:?
現(xiàn)在我們在main模塊中引入github.com/go-redis/redis 模塊,然后查看go.mod文件,發(fā)現(xiàn)有如下間接的依賴模塊,這里的模塊正是在github.com/go-redis/redis 中引入的模塊,可以查看github.com/go-redis/redis 模塊的go.mod文件以確認。
module github.com/go-redis/redis
當主版本升級到2時,則go.mod中的模塊導(dǎo)入名稱應(yīng)該為:
module github.com/go-redis/redis/v2
如果不增加v2這個標識,那么當使用go get github.com/go-redis/redis 下載包的時候,go會找到模塊名稱沒有使用主版本標識的最新的版本。我們通過查看該模塊在git上的6.15.9的版本源碼,發(fā)現(xiàn)其源碼中并沒有g(shù)o.mod文件。所以,當模塊的go.mod文件中的導(dǎo)入路徑?jīng)]有版本后綴(例如v2)的情況下,默認是v1版本,因此在使用go get獲取這樣的模塊時,默認會獲取v1.x.x的最新版本。五、 小版本的選擇
我們已經(jīng)知道了Go可以同時導(dǎo)入主版本不同的module。那么,如果只有小版本或補丁版本不同,那么Go該如何選擇呢?假設(shè)工程項目直接依賴于兩個module:A和B。同時A依賴于MODULE 1?的v1.0.1版本,但B依賴于MODULE 1的v1.0.2版本。如下圖所示:?
那么,在工程項目模塊(PROJECT MODULE)中需要間接依賴MODULE 1的哪個版本呢?如果我們使用v1.0.1,那么MODULE B有可能會產(chǎn)生異常。在語義化版本中,我們知道小版本或補丁版本應(yīng)該向后兼容,即v1.0.2是兼容v1.0.1的,所以在PROJECZT MODULE中應(yīng)該選擇MODULE 1的v1.0.2版本。
總結(jié)
Go module不僅解決了項目代碼不再依賴于GOPATH路徑,而且還解決了相同module的多版本引入問題。通過本篇文章,相信您對module的創(chuàng)建、發(fā)布、版本管理、依賴關(guān)系都會有了一個清晰的認識。想要了解更多 Golang 相關(guān)的內(nèi)容,歡迎掃描下方???關(guān)注?公眾號,回復(fù)關(guān)鍵詞 [實戰(zhàn)群]? ,就有機會進群和我們進行交流~
評論
圖片
表情
