Go mod 使用

【導讀】本文總結了 go mod 的使用和開發中的規範與最佳實踐。

最近由於換工作,開始交接工作。整理以前的工作內容,由於組內就我一個在做 go 和大數據。所以開發沒有規劃,當時是怎麼快怎麼來。go 也是使用最傳統的 go path 的方式管理的。都是手動管理依賴的。現在交接給他人,需要多人開發,發現很多問題。比如版本問題,各種依賴的問題等等。

由於工作原因,幾乎所有主流語言都寫過。所以,對應語言包管理工具也都瞭解和使用過。我前面有寫過 maven 的使用。maven 是使用過的功能最強大的包管理工具了,maven 定位是項目管理工具。pip 和 npm 都是及格的產品。

我個人覺得,一個包管理工具應該有以下功能:基本功能

  1. 依賴管理

  2. 依賴包版本控制

  3. 對應的包管理平臺

  4. 可以私有化部署

加分:

  1. 代碼包是否可以複用

  2. 構建,測試,打包

  3. 發佈上線

對比上面幾點:目前做的最好的也就 maven 了,gradle 沒有使用過,不知道。

今天主角是 go mod,先來談談沒有使用 go mod 之前的問題。

使用 go path 問題

  1. 代碼開發必須在 go path src 目錄下,不然,就有問題。

  2. 依賴手動管理

  3. 依賴包沒有版本可言

從這個看, go path 不算包管理工具

govendor

  1. 解決了包依賴,一個配置文件就管理

  2. 依賴包全都下載到項目 vendor 下,每個項目都把有一份。拉取項目時,開始懷疑人生。

go mod 介紹

go modules 是 golang 1.11 新加的特性。現在 1.12 已經發布了,是時候用起來了。Modules 官方定義爲:

模塊是相關 Go 包的集合。modules 是源代碼交換和版本控制的單元。go 命令直接支持使用 modules,包括記錄和解析對其他模塊的依賴性。modules 替換舊的基於 GOPATH 的方法來指定在給定構建中使用哪些源文件。

如何使用 go mod

首先,必須升級 go 到 1.11, 目前版本是 1.14 下面我以我自己升級演示:

### 卸載舊版本,刪除對應文件
brew uninstall -f go

### 更新一下 brew

brew update

### 安裝 go
brew install go

上面升級完了,使用 go version看下版本

go version go1.14.1 darwin/amd64

下面設置 go mod 和 go proxy

go env -w GOBIN=/Users/youdi/go/bin
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct // 使用七牛雲的

注意:go env -w 會將配置寫到  GOENV="/Users/youdi/Library/Application Support/go/env"

下面看下我的配置

GO111MODULE="on"
GOARCH="amd64"
GOBIN="/Users/youdi/go/bin"
GOCACHE="/Users/youdi/Library/Caches/go-build"
GOENV="/Users/youdi/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/youdi/go"
GOPRIVATE=""
GOPROXY="https://goproxy.cn,direct"
GOROOT="/usr/local/go"
GOSUMDB="off"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/8m/v_1j4dgs7rzgqq4p_4_8k_nr0000gn/T/go-build221113671=/tmp/go-build -gno-record-gcc-switches -fno-common"

我們看一下,我修改的內容

cat /Users/youdi/Library/Application Support/go/env

GO111MODULE=on
GOBIN=/Users/youdi/go/bin
GOPROXY=https://goproxy.cn,direct
GOSUMDB=off

GO111MODULE

GO111MODULE 有三個值:off, on 和 auto(默認值)。

GO111MODULE=off,go 命令行將不會支持 module 功能,尋找依賴包的方式將會沿用舊版本那種通過 vendor 目錄或者 GOPATH 模式來查找。GO111MODULE=on,go 命令行會使用 modules,而一點也不會去 GOPATH 目錄下查找。GO111MODULE=auto,默認值,go 命令行將會根據當前目錄來決定是否啓用 module 功能。這種情況下可以分爲兩種情形:

當前目錄在 GOPATH/src 之外且該目錄包含 go.mod 文件
當前文件在包含 go.mod 文件的目錄下面。

當 modules 功能啓用時,依賴包的存放位置變更爲 $GOPATH/pkg,允許同一個 package 多個版本並存,且多個項目可以共享緩存的 module

我們看下目錄:

cd /Users/youdi/go/pkg

├── darwin_amd64
│   ├── github.com
│   ├── go.etcd.io
│   ├── golang
│   ├── golang.org
│   ├── gopkg.in
│   ├── quickstart
│   └── uc.a
├── mod
│   ├── cache
│   ├── github.com
│   ├── golang.org
│   ├── google.golang.org
│   └── gopkg.in
└── sumdb
    └── sum.golang.org

go mod 命令

golang 提供了 go mod命令來管理包。

go help mod

Go mod provides access to operations on modules.

Note that support for modules 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 done using 'go get'.
See 'go help modules' for an overview of module functionality.

Usage:

    go mod <command> [arguments]

The commands are:

    download    download modules to local cache
    edit        edit go.mod from tools or scripts
    graph       print module requirement graph
    init        initialize new module in current directory
    tidy        add missing and remove unused modules
    vendor      make vendored copy of dependencies
    verify      verify dependencies have expected content
    why         explain why packages or modules are needed

Use "go help mod <command>" for more information about a command.

go mod 有以下命令:

VgojeW

比較常用的是 init,tidy, edit

使用 go mod 管理一個新項目

1. 初始化項目

可以隨便找一個目錄創建項目,我使用習慣用 IDEA 進行創建

mkdir Gone
cd Gone
go mod init Gone

查看一下 go.mod 文件

module Gone

go 1.14

go.mod 文件一旦創建後,它的內容將會被 go toolchain 全面掌控。go toolchain 會在各類命令執行時,比如 go get、go build、go mod 等修改和維護 go.mod 文件。

go.mod 提供了 module, require、replace 和 exclude 四個命令

2. 添加依賴

創建 main.go 文件

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message""pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

執行 go run main.go 運行代碼會發現 go mod 會自動查找依賴自動下載 再查看 go.mod

module Gone

go 1.14

require github.com/gin-gonic/gin v1.6.3

go module 安裝 package 的原則是先拉最新的 release tag,若無 tag 則拉最新的 commit

go 會自動生成一個 go.sum 文件來記錄 dependency tree

img

再次執行腳本 go run main.go 發現跳過了檢查並安裝依賴的步驟。

可以使用命令 go list -m -u all 來檢查可以升級的 package,使用 go get -u need-upgrade-package 升級後會將新的依賴版本更新到 go.mod * 也可以使用 go get -u 升級所有依賴

去 mod 包緩存下看看

/Users/youdi/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3

go get 升級

使用 replace 替換無法直接獲取的 package

由於某些已知的原因,並不是所有的 package 都能成功下載,比如:golang.org 下的包。

modules 可以通過在 go.mod 文件中使用 replace 指令替換成 github 上對應的庫,比如:

replace (
    golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)

go mod 發佈和使用

參考 Roberto Selbach 寫的 go mod 入門文章,文末,我給出鏈接

Creating a Module

如果你設置好 go mod 了,那你就可以在任何目錄下隨便創建

$mkdir gomodone
$cd gomodone

在這個目錄下創建一個文件say.go

package gomodone

import "fmt" 

// say Hi to someone
func SayHi(name string) string {
   return fmt.Sprintf("Hi, %s", name)
}

初始化一個 go.mod文件

$ go mod init github.com/jacksonyoudi/gomodone
go: creating new go.mod: module github.com/jacksonyoudi/gomodone

查看 go.mod 內容如下:

github.com/jacksonyoudi/gomodone
go 1.14

下面我們要將這個 module 發佈到 github 上,然後在另外一個程序使用

$git init
$vim .gitiiignore
$git commit -am "init"
// github 創建對應的 repo
$git remote add origin git@github.com:jacksonyoudi/gomodone.git
$git push -u origin master

執行完,上面我們就相當於發佈完了。

如果有人需要使用,就可以使用

go get github.com/jacksonyoudi/gomodone

這個時候沒有加 tag,所以,沒有版本的控制。默認是 v0.0.0 後面接上時間和 commitid。如下:

gomodone@v0.0.0-20200517004046-ee882713fd1e

官方不建議這樣做,沒有進行版本控制管理。

module versioning

使用 tag,進行版本控制

making a release

git tag v1.0.0
git push --tags

操作完,我們的 module 就發佈了一個 v1.0.0 的版本了。

推薦在這個狀態下,再切出一個分支,用於後續 v1.0.0 的修復推送,不要直接在 master 分支修復

$git checkout -b v1
$git push -u origin v1

use our module

上面已經發布了一個 v1.0.0 的版本,我們可以在另一個項目中使用,創建一個 go 的項目

$mkdir Gone
$cd Gone
$vim main.go
package main

import (
    "fmt"
    "github.com/jacksonyoudi/gomodone"
)

func main() {
    fmt.Println(gomodone.SayHi("Roberto"))
}

代碼寫好了,我們生成 go mod 文件

go mod init Gone

上面命令執行完,會生成 go mod 文件 看下 mod 文件:

module Gone

go 1.14

require (
    github.com/jacksonyoudi/gomodone v1.0.0
)
$go mod tidy
go: finding module for package github.com/jacksonyoudi/gomodone
go: found github.com/jacksonyoudi/gomodone in github.com/jacksonyoudi/gomodone v1.0.0

同時還生成了 go.sum, 其中包含軟件包的哈希值,以確保我們具有正確的版本和文件。

github.com/jacksonyoudi/gomodone v1.0.1 h1:jFd+qZlAB0R3zqrC9kwO8IgPrAdayMUS0rSHMDc/uG8=
github.com/jacksonyoudi/gomodone v1.0.1/go.mod h1:XWi+BLbuiuC2YM8Qz4yQzTSPtHt3T3hrlNN2pNlyA94=
github.com/jacksonyoudi/gomodone/v2 v2.0.0 h1:GpzGeXCx/Xv2ueiZJ8hEhFwLu7xjxLBjkOYSmg8Ya/w=
github.com/jacksonyoudi/gomodone/v2 v2.0.0/go.mod h1:L8uFPSZNHoAhpaePWUfKmGinjufYdw9c2i70xtBorSw=

這個內容是下面的,需要操作執行的結果

go run main.go 就可以運行了

Making a bugfix release

假如 fix 一個 bug, 我們在 v1 版本上進行修復

修改代碼如下:

// say Hi to someone
func SayHi(name string) string {
-       return fmt.Sprintf("Hi, %s", name)
+       return fmt.Sprintf("Hi, %s!", name)
}

修復好,我們開始 push

$ git commit -m "Emphasize our friendliness" say.go
$ git tag v1.0.1
$ git push --tags origin v1

Updating modules

剛纔 fix bug,所以要在我們使用項目中更新

這個需要我們手動執行更新 module 操作

我們通過使用我們的好朋友來做到這一點 go get:

目前 module 最新的也是 v1.0.1

// 更新最新
$go get -u
$go get -u=patch
//指定包,指定版本
$go get github.com/jacksonyoudi/gomodone@v1.0.1

操作完,go.mod 文件會修改如下:

module Gone

go 1.14

require (
    github.com/jacksonyoudi/gomodone v1.0.1
)

Major versions

根據語義版本語義,主要版本與次要版本  不同。主要版本可能會破壞向後兼容性。從 Go 模塊的角度來看,主要版本是  完全不同的軟件包。乍一看這聽起來很奇怪,但這是有道理的:兩個不兼容的庫版本是兩個不同的庫。比如下面修改,完全破壞了兼容性。

package gomodone

import (
    "errors"
    "fmt"
)

// Hi returns a friendly greeting
// Hi returns a friendly greeting in language lang
func SayHi(name, lang string) (string, error) {
    switch lang {
    case "en":
        return fmt.Sprintf("Hi, %s!", name), nil
    case "pt":
        return fmt.Sprintf("Oi, %s!", name), nil
    case "es":
        return fmt.Sprintf("¡Hola, %s!", name), nil
    case "fr":
        return fmt.Sprintf("Bonjour, %s!", name), nil
    default:
        return "", errors.New("unknown language")
    }
}

如上,我們需要不同的大版本,這種情況下

修改 go.mod 如下

module github.com/jacksonyoudi/gomodone/v2

go 1.14

然後,重新 tag,push

$ git commit say.go -m "Change Hi to allow multilang"
$ git checkout -b v2 # 用於 v2 版本,後續修復 v2
$ git commit go.mod -m "Bump version to v2"
$ git tag v2.0.0
$ git push --tags origin v2

Updating to a major version

即使發佈了庫的新不兼容版本,現有軟件 也不會中斷,因爲它將繼續使用現有版本 1.0.1。go get -u 將不會獲得版本 2.0.0。如果想使用 v2.0.0, 代碼改成如下:

package main

import (
    "fmt"
    "github.com/jacksonyoudi/gomodone/v2"
)

func main() {
    g, err := gomodone.SayHi("Roberto""pt")
    if err != nil {
        panic(err)
    }
    fmt.Println(g)
}

執行 go mod tidy

go: finding module for package github.com/jacksonyoudi/gomodone/v2
go: downloading github.com/jacksonyoudi/gomodone/v2 v2.0.0
go: found github.com/jacksonyoudi/gomodone/v2 in github.com/jacksonyoudi/gomodone/v2 v2.0.0

當然,兩個版本都可以同時使用,使用別名 如下:

package main

import (
    "fmt"
    "github.com/jacksonyoudi/gomodone"
    mv2 "github.com/jacksonyoudi/gomodone/v2"
)

func main() {
    g, err := mv2.SayHi("Roberto""pt")
    if err != nil {
        panic(err)
    }
    fmt.Println(g)

    fmt.Println(gomodone.SayHi("Roberto"))
}

執行一下 go mod tidy

Vendoring

默認是忽略 vendor 的,如果想在項目目錄下有 vendor 可以執行下面命令

$go vendor

當然,如果構建程序的時候,希望使用 vendor 中的依賴,

$ go build -mod vendor

IDEA 下開發 GO

  1. 創建 go 項目

  1. 創建完項目,會自動生成 go mod 文件 如果需要修改,可以手動修改,加入 git 等操作

  2. 寫業務邏輯代碼

  3. 解決依賴,更新 go.mod

go build

轉自:

jianshu.com/p/760c97ff644c

Go 開發大全

參與維護一個非常全面的 Go 開源技術資源庫。日常分享 Go, 雲原生、k8s、Docker 和微服務方面的技術文章和行業動態。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Rzk3_JZ_ihk64eEd-MdBDw