go modules 初體驗

引言

通過上一篇文章《go modules 基礎》的學習穆役,很多讀者已經(jīng)掌握了 go 原生的包依賴管理方案的基本知識娜氏,于是在實踐中嘗試 go modules 機制的想法已箭在弦上或听,不得不發(fā)炕置。

本文開啟 go modules 的實戰(zhàn)部分变过,將帶領讀者通過一段輕松的旅途沐扳,初步體驗一下 go modules(簡稱 go mod) 的強大汤踏。

go mod 工程的搭建

我們考慮在本地搭建一個 go mod 工程织鲸,工程名為 hello-gomod,而工程所在的位置應該是任意的溪胶,且不受原有 GOPATH mode 的影響搂擦。

本地 GOPATH 的路徑:

$ echo $GOPATH
/Users/zhangxiaolong/Desktop/D/go-workspace

我們先在 D 盤建立一個新目錄 gomod(與 GOPATH 的路徑無關),然后在 gomod 目錄下建立一個目錄 hello-gomod(工程名)哗脖。

打開 shell 終端瀑踢,在 hello-gomod 目錄下運行 go mod init 命令:

$ go mod init github.com/agiledragon/hello-gomod
go: creating new go.mod: module github.com/agiledragon/hello-gomod
$ cat go.mod 
module github.com/agiledragon/hello-gomod

go 1.14

我們發(fā)現(xiàn)在 hell-gomod 目錄下生成了一個文件 go.mod,該文件中的內(nèi)容為:

  • 第一行為該工程的模塊名 github.com/agiledragon/hello-gomod才避。一般模塊命名為該工程所在代碼倉庫的路徑橱夭,比如作者一般將代碼工程放在 github 上的個人目錄下
  • 第二行為本地 go 語言的版本。作者本地的 go 語言版本為 1.14工扎,go mod 機制已成熟

在該工程下新建一個包 calculator徘钥,并實現(xiàn)一個簡單的函數(shù) add:

package calculator

func Add(a, b int) int {
    return a + b
}

在 main 包中調(diào)用 math 包的 Add 函數(shù):

package main

import (
    "fmt"

    "github.com/agiledragon/hello-gomod/calculator"
)

func main() {
    r := 1
    r = calculator.Add(1, 2)
    fmt.Println(r)
}

可見,在本工程中導入工程內(nèi)部的包時的路徑為:"模塊名" + "/" + "內(nèi)部包的相對路徑"肢娘。

至此呈础,hello-gomod 工程下的目錄結(jié)構(gòu)為:

$ find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
.
|____go.mod
|____calculator
| |____add.go
|____main.go

注:Mac 下默認沒有 tree 命令,我們通過 find 命令來模擬橱健。

在 hello-gomod 目錄下運行工程:

$ go run main.go 
3

注:該模塊還沒有依賴任意的第三方包而钞,所以在工程目錄下沒有生成 go.sum 文件,同時 go.mod 文件也沒有任何變化拘荡。

go mod 工程的測試

為了提高代碼質(zhì)量臼节,我們計劃為 calculator 包添加單元測試,于是新建了在 calculator 包下新建了測試文件 add_test.go珊皿,并寫了一個簡單的測試用例:

package calculator

import (
    "testing"

    "github.com/smartystreets/goconvey/convey"
)

func TestAdd(t *testing.T) {
    convey.Convey("TestAdd", t, func() {
        convey.Convey("normal case", func() {
            convey.So(3, convey.ShouldEqual, Add(1, 2))
        })
    })
}

我們直接運行該測試:

$ go test -v ./calculator/
go: downloading github.com/smartystreets/goconvey v1.6.4
go: downloading github.com/jtolds/gls v4.20.0+incompatible
go: downloading github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d
=== RUN   TestAdd

  TestAdd 
    normal case ?


1 total assertion

--- PASS: TestAdd (0.00s)
PASS
ok      github.com/agiledragon/hello-gomod/calculator   (cached)

我們發(fā)現(xiàn)測試運行成功网缝,并且 go test 命令在正式測試前下載了所有的依賴項。

我們再運行一下測試:

$ go test -v ./calculator/
=== RUN   TestAdd

  TestAdd 
    normal case ?


1 total assertion

--- PASS: TestAdd (0.00s)
PASS
ok      github.com/agiledragon/hello-gomod/calculator   0.006s

我們發(fā)現(xiàn)本次 go test 運行前不再下載依賴項蟋定,那說明依賴項用的是本地的(上次測試下載的)粉臊。

這些依賴項是從哪下載的?讀者可能也想到了環(huán)境變量 GOPROXY驶兜,我們一起看一下:

$ echo $GOPROXY
https://goproxy.cn,direct

注:goproxy.cn 是七牛云維護的一個非營利性項目扼仲,目標是為中國及世界上其他地方的 gophers 們提供一個免費的远寸、可靠的、持續(xù)在線的且經(jīng)過 CDN 加速的模塊代理屠凶;“direct” 為特殊指示符驰后,用于指示 go 回源到模塊版本的源地址去抓取 (比如 github 等),當值列表中上一個 go module proxy 返回 404 或 410 錯誤時矗愧,go 會自動嘗試列表中的下一個灶芝,遇見 “direct” 時回源,遇見 EOF 時終止并拋出類似 “invalid version: unknown revision...” 的錯誤唉韭。

這些依賴項都下載到哪去了监署?依賴項是如何管理的?我們雖然對整個過程不是很清楚纽哥,但可以斷定這些問題肯定與 go.mod 文件有關。

我們先查看 go.mod 文件栖秕,發(fā)現(xiàn)多了一行:

module github.com/agiledragon/hello-gomod

go 1.14

require github.com/smartystreets/goconvey v1.6.4

注:最后一行是新增的春塌,require 是關鍵字,指明 hello-gomod 工程的依賴項簇捍,此處只有 goconvey 模塊(代碼工程)及其版本只壳。

我們接著查看 hello-gomod 工程的目錄,發(fā)現(xiàn)模塊根目錄下(與 go.mod 同級)增加了一個文件 go.sum:

$ cat go.sum 
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

注:本 go.sum 文件是運行 go test 命令自動生成的暑塑,里面有直接依賴和間接依賴的所有模塊的導入路徑吼句、版本號和哈希值,可以確保下次獲取的第三方依賴及其版本與本次完全相同事格,即保證了可重復構(gòu)建惕艳。

我們最后進入 $GOPATH/pkg 目錄,發(fā)現(xiàn)多了一個 mod 子目錄驹愚,同時 mod 下有兩個子目錄:

$ pwd
/Users/zhangxiaolong/Desktop/D/go-workspace/pkg/mod
zhangxiaolongdeMacBook-Pro:mod zhangxiaolong$ ls
cache       github.com

注:細節(jié)先不用深究,我們僅知道 $GOPATH/pkg/mod 目錄下是 go mod 的緩存就可以了。當運行 go run把跨,go build们拙,go test 等命令時,如果 go mod 緩存中有依賴項劫瞳,則直接獲取倘潜,否則先拉取到緩存,然后再從緩存中獲取志于。

當用戶使用了 go mod 機制管理依賴包后涮因,就棄用了 GOPATH mode,而這時 go mod 的緩存仍然放在 $GOPATH/pkg/mod 目錄下恨憎。貌似 go mod 與 環(huán)境變量 GOPATH 仍然有一點點關系蕊退,但這種關系非常弱郊楣,因為用戶可以在任意目錄放代碼工程,而不再局限在 $GOPATH/src 目錄下了瓤荔。

更進一步净蚤,如果我們不配置環(huán)境變量 GOPATH ,go mod 緩存會存放在哪里输硝?

我們先取消環(huán)境變量 GOPATH 的設置:

$ echo $GOPATH
/Users/zhangxiaolong/Desktop/D/go-workspace
$ unset GOPATH
$ echo $GOPATH

$ 

我們再運行一下測試今瀑,發(fā)現(xiàn)測試仍然通過,并且 go test 命令在正式測試前下載了所有的依賴項:與我們設置了 GOPATH 環(huán)境變量的情況完全相同

$ go test -v ./calculator/
go: downloading github.com/smartystreets/goconvey v1.6.4
go: downloading github.com/jtolds/gls v4.20.0+incompatible
go: downloading github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d
=== RUN   TestAdd

  TestAdd 
    normal case ?


1 total assertion

--- PASS: TestAdd (0.00s)
PASS
ok      github.com/agiledragon/hello-gomod/calculator   0.008s

注:我們?nèi)∠?GOPATH 環(huán)境變量后点把,go mod 緩存就會自動下載到 $HOME/go/pkg/mod 目錄下橘荠。本質(zhì)上,$HOME/go 就是 GOPATH 變量的值(從 go1.8 版本開始郎逃,GOPATH 變量有了默認值 $HOME/go)哥童。

go mod 工程的 CI

go mod 工程上線 CI 后,代碼中沒有了存放第三方依賴項的 vendor 目錄褒翰,編譯時需要實時拉取第三方依賴贮懈。

這里有兩個問題:

  • CI 運行過程中不能訪問大網(wǎng)(公網(wǎng))
  • CI 運行過程中對性能和可靠性要求高,從大網(wǎng)下載所有依賴項有風險

針對這些問題优训,有兩種解決方案:

  • 使用 go mod vendor 命令生成一個 vendor 目錄朵你,并上傳到代碼庫
  • 搭建一個私有的 go proxy

關于如何搭建一個私有的 go proxy,本文不進行深入討論揣非,在后續(xù)的文章中可能會涉及抡医。

我們在模塊根目錄下運行 go mod vendor 命令:

$ go mod vendor
go: downloading github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1

注:我們運行 go test 命令時,沒有下載 gopherjs 模塊早敬,而運行 go mod vendor 命令時忌傻,自動下載了一個新的依賴項。

我們進入到 vendor 目錄搁嗓,并查看子目錄:

$ cd vendor
$ ls
github.com  modules.txt

一個代碼工程從 GOPATH mode 遷移到 module-aware(go mod) mode 時芯勘,可能的三種狀態(tài):

  • state1:GOPATH mode vendor
  • state2:go.mod + go.sum + module-aware mode vendor
  • state3:go.mod + go.sum + go proxy

對于一個大型項目,有很多個團隊腺逛,而每個團隊又有多個 go 工程荷愕,作者建議遷移 go mod 時增加臨時狀態(tài) state2,即工程狀態(tài)變化為:state1 ==> state2 ==> state3棍矛;對于一個小型項目安疗,總共只有幾個 go 工程,作者建議遷移 go mod 時直接到目標狀態(tài) state3够委,即工程狀態(tài)變化為:state1 ==> state3荐类。

小結(jié)

本文通過一個簡單的案例,帶領讀者初步體驗了 go mod 機制的強大茁帽。讀者在輕松愉悅的旅途中理清了 go mod 的基本原理玉罐,掌握了 go mod 的簡單用法屈嗤,后續(xù)可以嘗試在學習或工作中使用 go mod 來管理工程的依賴項,從而真實感受 go mod 的魅力吊输。

下一篇文章《gomonkey 的 go mod 改造之旅》預告:
gomonkey 是作者自研的一款 go 語言的打樁框架饶号,深受國內(nèi)外 gophers 的喜愛,目前已有 336 個 star季蚂。gomonkey 在運行時不依賴第三方包茫船,長期以來一直保持 GOPATH mode 并不會出現(xiàn)任何問題。gomonkey 提供了詳盡的測試用例扭屁, 雖然運行這些用例需要依賴第三方包(測試框架)算谈,但對于大多數(shù)用戶來說他們并不實際運行這些用例,而是僅僅將這些用例當作 API 文檔來學習料滥。我們嘗試用 go mod 機制來改造 gomonkey 工程然眼,希望能給讀者帶來更佳的用戶體驗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葵腹,一起剝皮案震驚了整個濱河市罪治,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌礁蔗,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雁社,死亡現(xiàn)場離奇詭異浴井,居然都是意外死亡,警方通過查閱死者的電腦和手機霉撵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門磺浙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人徒坡,你說我怎么就攤上這事撕氧。” “怎么了喇完?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵伦泥,是天一觀的道長。 經(jīng)常有香客問我锦溪,道長不脯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任刻诊,我火速辦了婚禮防楷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘则涯。我一直安慰自己复局,他們只是感情好冲簿,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著亿昏,像睡著了一般峦剔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上龙优,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天羊异,我揣著相機與錄音,去河邊找鬼彤断。 笑死野舶,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的宰衙。 我是一名探鬼主播平道,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼供炼!你這毒婦竟也來了一屋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤袋哼,失蹤者是張志新(化名)和其女友劉穎冀墨,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涛贯,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡诽嘉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了弟翘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虫腋。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖稀余,靈堂內(nèi)的尸體忽然破棺而出悦冀,到底是詐尸還是另有隱情,我是刑警寧澤睛琳,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布盒蟆,位于F島的核電站,受9級特大地震影響师骗,放射性物質(zhì)發(fā)生泄漏茁影。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一丧凤、第九天 我趴在偏房一處隱蔽的房頂上張望募闲。 院中可真熱鬧,春花似錦愿待、人聲如沸浩螺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽要出。三九已至鸳君,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間患蹂,已是汗流浹背或颊。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留传于,地道東北人囱挑。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像沼溜,于是被迫代替她去往敵國和親平挑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354