<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手把手教你如何創(chuàng)建及使用Go module

          共 6612字,需瀏覽 14分鐘

           ·

          2022-04-01 21:13

          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是如何安裝依賴包的,然后再分析這種包管理的不足。

          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。
          根據(jù)讀取到的meta信息,Go就可以從 https://github.com/go-redis/redis.git 中克隆該項目代碼,并將其保存到本地的$GOPATH/src目錄下的github.com/go-redis/redis中。

          92e485f9bfc76c8ec36c014e46dc1caf.webp

          到此,我們已經(jīng)了解了傳統(tǒng)的包管理的工作方式了。下面我們來看看這種管理方式有哪些缺點。1.2 傳統(tǒng)包管理方式的不足首先,所有的項目都必須在GOPATH/src指向的目錄下,或者必須更改GOPATH環(huán)境變量所指向的目錄。我們以兩個項目A、B來舉例說明。假設(shè)當前的GOPATH=/usr/local/goworkspace/。如果保持GOPATH不變的話,那么A、B兩個項目的源代碼都必須要放到GOPATH的目錄下,即/usr/local/goworkspace/src目錄下。同時,A和B項目引入的第三方包都會在GOPATH/pkg目錄下。這樣兩個項目其實就是混合在一起。如果不想混合在一起怎么辦呢?那就只能更改GOPATH的目錄。假設(shè)我們現(xiàn)在在研發(fā)A項目,并將其工作目錄放在/usr/local/goworkspace/a目錄下,GOPATH=/usr/local/goworkspace/a。但是在開發(fā)B項目時,更改GOPATH的指向,例如我們這里使用/usr/local/goworkspace/b目錄下。這樣兩個項目的源代碼以及依賴的第三方包就在各自項目下了。但同時如果想繼續(xù)修改A項目的代碼時,就需要再將GOPATH目錄更改到指向A項目的目錄中,即GOPATH=/usr/local/goworkspace/src目錄。其次,對于依賴的同一個包只能從master分支上導(dǎo)入最新的提交,且不能導(dǎo)入包的指定的版本。假設(shè)我們有一個第三方包redis,項目A首次引入該包時,使用go get命令從代碼庫的master分支下載當前最新的代碼,并將該包保存在本地的GOPATH/pkg目錄下。之后redis包有了新的提交,但同時也引入了一個bug。如果項目A升級或重新安裝該包時,使用go get命令并沒有指定特定版本的參數(shù),還是從該包的代碼庫的master分支中下載該包,也就造成了向后不兼容。另外,升級或重新安裝的包也會被安裝到GOPATH/pkg下的相同目錄,因為沒有版本的管理,所以會覆蓋之前。好了,以上就是在傳統(tǒng)的包管理方式中的兩大主要不足之處。那么針對這些不足,我們來看看Go的module是如何解決的。

          二、現(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也是可以像package一樣共享的。因此,module也必須是一個git倉庫或其他Go可支持的代碼控制系統(tǒng)。因此,Go的建議是:
          • 一個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等。如下圖:

          d767c8a28c0387ffc24f6990765ac1f7.webp

          根據(jù)上面所討論的,一個Go module應(yīng)該是一個版本控制系統(tǒng)上的代碼倉庫。所以我們在github上創(chuàng)建一個git的代碼倉庫,如下圖:

          b1eab4b2bae1f37a0f95d24917703bf4.webp

          第二,在本地的目錄下執(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)的版本。如下所示:

          a0ffe9e71f6de217052913d0000d68ae.webp

          由上圖可知,在生成的go.mod文件中顯示了該module可被導(dǎo)入的路徑以及Go的版本。因為目前還沒有導(dǎo)入任何其他依賴包,所以沒有顯示導(dǎo)入包的信息。好,現(xiàn)在我們把該目錄同時提交到git上。
          git init

          git remote add origin https://github.com/goxuetang/encodex.git

          第三,我們在encodex的hash包中添加如下代碼

          60a3b6417252e6427ce87847445f7fc6.webp

          好了,到這里我們就可以發(fā)布我們的包。但在發(fā)布之前我們先來看下語義化的版本。語義化的版本是一種通用的版本格式。其格式如下:
          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版本。接下來,我們看看在項目中如何使用該module

          2.4 如何使用第三方module

          我們在新建的main module中創(chuàng)建了一個main.go文件,在該module下要想使用encodex模塊下的包,則需要引入和安裝兩個步驟。在文件中使用import語句引入包,如下圖:?6d503d01efa819d5bff20f570726e047.webp第一步,使用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

          如圖所示:

          115da230882d9c73c6933887d635475d.webp

          同時,go get會將引入的包加在go.mod文件中。require中不僅有包名,還有對應(yīng)的版本號。如下圖所示:?c13d540459aab885a40148717de36e0e.webp好,我們現(xiàn)在來看另外一個問題,下載下來的包存在哪里了。2.5 module存儲在哪里當go get將包下載下來后,會將其存儲到GOPATH/pkg/mod目錄下。通過go env可以查看GOPATH環(huán)境變量的具體指向目錄,我的環(huán)境下的GOPATH=/Users/YuYang/go,如下是上節(jié)中引入的encodex模塊。如下圖所示:

          ee345d6fb3baa080e8e385deb3684696.webp

          我們發(fā)現(xiàn)encodex模塊的目錄是帶版本號的,這也是Go module能夠支持多版本的原因。三、如何升級版本在上面我們有講到module使用的是vX.X.X格式的語義化版本。那么在日常的研發(fā)中又是如何對這三個版本號進行升級的呢。3.1 如何升級module的小版本和補丁版本隨著時間的推移,發(fā)布的包肯定會有新的提交,比如修復(fù)了一個bug,則patch版本號會升級,添加了一個新功能,則小版本號會升級。做了一項大的改動,和前一個版本不兼容了,那么主版本號就會升級。接下來我們看看在已引入的包后,如何升級對應(yīng)的版本。如果我們只想升級補丁版本patch,那么可以使用如下命令:
          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)入的模塊。而間接依賴就是我們直接依賴的模塊所依賴的。如下圖:?e04362d2c57cb8e9abae5c188b9abdd3.webp現(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文件以確認。

          39517c689439685a7a55a12651a2210e.webp

          在上圖中,我們還發(fā)現(xiàn)redis的模塊后面的版本是 v6.15.9+incompatible。這個代表什么意思呢?這個代表的是引入的模塊的最新版本是v5.15.9,但同時具有不兼容的風險。為什么呢?因為在redis模塊中未使用規(guī)范的導(dǎo)入名稱。例如,規(guī)范的模塊命名應(yīng)該是在模塊的版本大于1的時候,導(dǎo)入名稱就需要增加主版本信息。例如,當該模塊是第一個版本時,其對應(yīng)的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版本。如下圖所示:?068965f5d3d7a07e1e9745652cc36ea9.webp那么,在工程項目模塊(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版本。

          dabf6672dbe27c2b4c9d130a7a3464da.webp

          總結(jié)

          Go module不僅解決了項目代碼不再依賴于GOPATH路徑,而且還解決了相同module的多版本引入問題。通過本文章,相信您對module的創(chuàng)建、發(fā)布、版本管理、依賴關(guān)系都會有了一個清晰的認識。
          想要了解更多 Golang 相關(guān)的內(nèi)容,歡迎掃描下方???關(guān)注?公眾號,回復(fù)關(guān)鍵詞 [實戰(zhàn)群]? ,就有機會進群和我們進行交流~



          瀏覽 125
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线观看免费欧美操逼视频 | 97久久人人超碰 | 国产精品视频卡一卡二 | 麻豆性爱| 欧美日日日日 |