go modules
是 golang 1.11 新加的特性,Modules官方定義爲:
模塊是相關Go包的集合。modules是源代碼交換和版本控制的單元。 go命令直接支持使用modules,
包括記錄和解析對其他模塊的依賴性。modules替換舊的基於GOPATH的方法來指定在給定構建中使
用哪些源文件。
使用
- 把 golang 升級到 1.11(現在1.12 已經發布了,建議使用1.12)
- 設置
GO111MODULE
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文件的目錄下面。
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文件一起提交。
在項目中使用
示例一.在新項目中使用
- 在
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
, require
、replace
和exclude
四個命令
module
語句指定包的名字(路徑)require
語句指定的依賴項模塊replace
語句可以替換依賴項模塊exclude
語句可以忽略依賴項模塊
- 添加依賴
新建一個 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=
... 省略很多行
複製代碼
- 再次執行腳本
go run server.go
發現跳過了檢查並安裝依賴的步驟。 - 可以使用命令
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")
}
複製代碼
- 使用
go mod init ***
初始化go.mod
$ go mod init helloworld
go: creating new go.mod: module helloworld
複製代碼
- 運行
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。
- 更新舊的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
🤷♂️
到這裏就和新創建一個項目沒什麼區別了
感謝閱讀!
歡迎大家關注我的公衆號