go mod 與 goproxy 的使用

go modules 是 golang 1.11 新加的特性,Modules官方定義爲:

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

 


使用 

  1. 把 golang 升級到 1.11(現在1.12 已經發布了,建議使用1.12)
  2. 設置 GO111MODULE

GO111MODULE

GO111MODULE 有三個值:off, onauto(默認值)

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

GO111MODULE解釋, 當爲on時則使用Go Modules,go 會忽略 $GOPATH和 vendor文件夾,只根據go.mod下載依賴。當爲 off時則不適用新特性 Go Modules支持,它會查找 vendor目錄和 $GOPATH來查找依賴關係,也就是繼續使用“GOPATH模式”。當爲 auto時或未設置時則根據當前項目目錄下是否存在 go.mod文件或 $GOPATH/src之外並且其本身包含go.mod文件時纔會使用新特性 Go Modules模式,並且auto爲 GO111MODULE的默認值。

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

go mod

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

go mod 有以下命令:

命令 說明
download download modules to local cache(下載依賴包)
edit edit go.mod from tools or scripts(編輯go.mod
graph print module requirement graph (打印模塊依賴圖)
init initialize new module in current directory(在當前目錄初始化mod)
tidy add missing and remove unused modules(拉取缺少的模塊,移除不用的模塊)
vendor make vendored copy of dependencies(將依賴複製到vendor下)
verify verify dependencies have expected content (驗證依賴是否正確)
why explain why packages or modules are needed(解釋爲什麼需要依賴)

goproxy
關於goproxy,簡單來說就是一個代理,讓我們更方便的下載哪些由於牆的原因而導致無法下載的第三方包,比如golang.org/x/下的包,雖然也有各種方法解決,但是,如果是你在拉取第三方包的時候,而這個包又依賴於golang.org/x/下的包,你本地又恰恰沒有,當然不嫌麻煩的話,也可以先拉取golang.org/x/下的包,再拉取第三方包。

這個goproxy強大地方就在於代理,而它官網是這樣介紹自己的【A Global Proxy for Go Modules】,就是這麼強大,全球代理,讓世界沒有難下的包


使用
一般來說,goproxy是和go modules配合使用的。
首先要確保go modules是開啓的,如果沒有開啓可以在命令行鍵入下面命令即可

export GO111MODULE=on
export GOPROXY=https://goproxy.io
1
2
不過這種是一次性的,重啓之後就會消失,所以可以在環境信息文件/etc/profile的最後面添加上下面這兩行

export GO111MODULE=on
export GOPROXY=https://goproxy.io
1
2
最後使用source /etc/profile讓其生效。


go.mod 和 go.sum介紹


go.mod是Go項目的依賴描述文件,該文件主要用來描述兩個事情:

當前項目名(module)是什麼。每個項目都應該設置一個名稱,當前項目中的包(package)可以使用該名稱進行相互調用。
當前項目依賴的第三方包名稱。項目運行時會自動分析項目中的代碼依賴,生成go.sum依賴分析結果,隨後go編譯器會去下載這些第三方包,然後再編譯運行。
go.sum依賴分析文件,記錄每個依賴庫的版本和哈希值
一般情況下,go.sum應當被添加到版本管理中隨着go.mod文件一起提交。


在項目中使用

示例一.在新項目中使用

   

 

  1. GOPATH 目錄之外新建一個目錄,並使用go mod init 初始化生成go.mod 文件
➜  ~ mkdir hello
➜  ~ cd hello
➜  hello go mod init hello
go: creating new go.mod: module hello
➜  hello ls
go.mod
➜  hello cat go.mod
module hello

go 1.12
複製代碼

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

go.mod 提供了module, requirereplaceexclude 四個命令

  • module 語句指定包的名字(路徑)
  • require 語句指定的依賴項模塊
  • replace 語句可以替換依賴項模塊
  • exclude 語句可以忽略依賴項模塊
  1. 添加依賴

新建一個 server.go 文件,寫入以下代碼:

package main

import (
	"net/http"
	
	"github.com/labstack/echo"
)

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	e.Logger.Fatal(e.Start(":1323"))
}
複製代碼

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

$ go run server.go
go: finding github.com/labstack/echo v3.3.10+incompatible
go: downloading github.com/labstack/echo v3.3.10+incompatible
go: extracting github.com/labstack/echo v3.3.10+incompatible
go: finding github.com/labstack/gommon/color latest
go: finding github.com/labstack/gommon/log latest
go: finding github.com/labstack/gommon v0.2.8
# 此處省略很多行
...

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.10-dev
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:1323
複製代碼

現在查看go.mod 內容:

$ cat go.mod

module hello

go 1.12

require (
	github.com/labstack/echo v3.3.10+incompatible // indirect
	github.com/labstack/gommon v0.2.8 // indirect
	github.com/mattn/go-colorable v0.1.1 // indirect
	github.com/mattn/go-isatty v0.0.7 // indirect
	github.com/valyala/fasttemplate v1.0.0 // indirect
	golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
)
複製代碼

go module 安裝 package 的原則是先拉最新的 release tag,若無tag則拉最新的commit,詳見 Modules官方介紹。 go 會自動生成一個 go.sum 文件來記錄 dependency tree:

$ cat go.sum
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
... 省略很多行
複製代碼
  1. 再次執行腳本 go run server.go 發現跳過了檢查並安裝依賴的步驟。
  2. 可以使用命令 go list -m -u all 來檢查可以升級的package,使用go get -u need-upgrade-package 升級後會將新的依賴版本更新到go.mod * 也可以使用 go get -u 升級所有依賴

go get 升級

  • 運行 go get -u 將會升級到最新的次要版本或者修訂版本(x.y.z, z是修訂版本號, y是次要版本號)
  • 運行 go get -u=patch 將會升級到最新的修訂版本
  • 運行 go get package@version 將會升級到指定的版本號version
  • 運行go get如果有版本的更改,那麼go.mod文件也會更改

示例二:改造現有項目(helloword)

項目目錄爲:

$ tree
.
├── api
│   └── apis.go
└── server.go

1 directory, 2 files
複製代碼

server.go 源碼爲:

package main

import (
    api "./api"  // 這裏使用的是相對路徑
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", api.HelloWorld)
    e.Logger.Fatal(e.Start(":1323"))
}
複製代碼

api/apis.go 源碼爲:

package api

import (
    "net/http"

    "github.com/labstack/echo"
)

func HelloWorld(c echo.Context) error {
    return c.JSON(http.StatusOK, "hello world")
}
複製代碼
  1. 使用 go mod init *** 初始化go.mod
$ go mod init helloworld
go: creating new go.mod: module helloworld
複製代碼
  1. 運行 go run server.go
go: finding github.com/labstack/gommon/color latest
go: finding github.com/labstack/gommon/log latest
go: finding golang.org/x/crypto/acme/autocert latest
go: finding golang.org/x/crypto/acme latest
go: finding golang.org/x/crypto latest
build command-line-arguments: cannot find module for path _/home/gs/helloworld/api
複製代碼

首先還是會查找並下載安裝依賴,然後運行腳本 server.go,這裏會拋出一個錯誤:

build command-line-arguments: cannot find module for path _/home/gs/helloworld/api
複製代碼

但是go.mod 已經更新:

$ cat go.mod
module helloworld

go 1.12

require (
        github.com/labstack/echo v3.3.10+incompatible // indirect
        github.com/labstack/gommon v0.2.8 // indirect
        github.com/mattn/go-colorable v0.1.1 // indirect
        github.com/mattn/go-isatty v0.0.7 // indirect
        github.com/valyala/fasttemplate v1.0.0 // indirect
        golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
)
複製代碼

那爲什麼會拋出這個錯誤呢?

這是因爲 server.go 中使用 internal package 的方法跟以前已經不同了,由於 go.mod會掃描同工作目錄下所有 package 並且變更引入方法,必須將 helloworld當成路徑的前綴,也就是需要寫成 import helloworld/api,以往 GOPATH/dep 模式允許的 import ./api 已經失效,詳情可以查看這個 issue

  1. 更新舊的package import 方式

所以server.go 需要改寫成:

package main

import (
    api "helloworld/api"  // 這是更新後的引入方法
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", api.HelloWorld)
    e.Logger.Fatal(e.Start(":1323"))
}
複製代碼

一個小坑:開始在golang1.11 下使用go mod 遇到過 go build github.com/valyala/fasttemplate: module requires go 1.12 這種錯誤,遇到類似這種需要升級到1.12 的問題,直接升級golang1.12 就好了。幸虧是在1.12 發佈後才嘗試的go mod 🤷‍♂️

到這裏就和新創建一個項目沒什麼區別了

感謝閱讀!


歡迎大家關注我的公衆號

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章