1. 程式人生 > >Go Modules 踩坑總結

Go Modules 踩坑總結

微信公眾號「後端進階」,專注後端技術分享:Java、Golang、WEB框架、分散式中介軟體、服務治理等等。

在 Java 的專案中,有 Maven 和 Gradle 這些很好用的依賴版本管理工具,簡直不要太方便了,但是在 Golang 的專案中,之前的 Golang 官方並沒有提供版本管理工具,我們以前用 go get 獲取依賴其實是有潛在危險的,因為我們不確定最新版依賴是否會破壞掉我們專案對依賴包的使用方式,即當前專案可能會出現不相容最新依賴包的問題。之後官方出了一個 vendor 機制,將專案依賴的包都放在該目錄中,但這也並沒有很好地管理依賴的版本。之後官方出了一個準官方版本管理工具 go dep,這也算是 go modules 的前身了吧。隨著 Go1.11 的釋出,Golang 給我們帶來了 module 全新特性,這是 Golang 新的一套依賴管理系統。現在 Go1.12 已經發布了,go modules 進一步穩定,但官方還是沒有將其設為預設機制,所以踩坑之路是必須的,本篇文章除了詳細說明 go modules 的特性以及使用之外,還總結了我在這個過程中遇到的一些“坑”。

建立 module

  • 建立專案

在預設情況下,$GOPATH 預設情況下是不支援 go mudules 的,我們需要在專案目錄下手動執行以下命令:

$ export GO111MODULE=on

這也表明了 go 要利用 modules 機制消滅 $GOPATH 的決心啊!

為了配合 go modules 機制,我們 $GOPATH 以外的目錄建立一個 testmod 的包:

$ mkdir testmod
$ cd testmod

$ echo 'package testmod
		import "fmt"
	func SayHello(name string) string {
   return fmt.Sprintf("Hello, %s", name)
}' >> testmod.go
  • 初始化 module:
$ go mod init github.com/objcoding/testmod
go: creating new go.mod: module github.com/objcoding/testmod

以上命令會在專案中建立一個 go.mod 檔案,初始化內容如下:

module github.com/objcoding/testmod

這時,我們的專案已經成為了一個 module 了。

  • 推送到 github 倉庫
$ git init
$ git add *
$ git commit -am "First commit"
$ git push -u origin master

在這裡我也著重說下關於專案依賴包引用地址的問題,這個問題雖小,但也確實很困擾人,所以必須得說一下:

go mudules 出現之前,在一個專案中有很多個包,在專案內,有些包需要依賴專案內其它包,假設專案有個包,相對於 gopath 的地址是 objcoding/mypackage,在專案內其它包引用這個包時,就可以通過以下引用:

import myproject/mypackage

但你有沒有想過,當別的專案需要引用你的專案中的某些包,那麼就需要遠端下載依賴包了,這時就需要專案的倉庫地址引用,比如下面這樣:

import github.com/objcoding/myproject/mypackage

go modules 釋出之後,就完全統一了包引用的地址,如上面我們說的建立 go.mod 檔案後,初始化內容的第一行就是我們說的專案依賴路徑,通常來說該地址就是專案的倉庫地址,所有需要引用專案包的地址都填寫這個地址,無論是內部之間引用還是外部引用,舉個例子,goim 的內部包引用:

go.mod module:

內部包引用:

也即是說,在專案 啟用了 go modules 之後,引用包必須跟 go mod 檔案第一行包名一樣,

依賴的包都會儲存在 ${GOPATH}/pkg/mod 資料夾中了,我們也可以在專案底部那裡檢視依賴包:

但也有可能會出現依賴包地址正確但會報紅的情況,這時極有可能是你在 Goland 編輯器中沒有將專案設定為 go modules 專案,具體設定如下:

勾選了該選項之後,就會在 External Libraries 中出現 Go Modules 目錄。

版本規則

go modules 是一個版本化依賴管理系統,版本需要遵循一些規則,比如版本號需要遵循以下格式:

vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef
vX.0.0-yyyymmddhhmmss-abcdefabcdef
vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef
vX.Y.Z

vX.Y.Z 是我們倉庫打的標籤版本,也就是 go modules 是根據倉庫標籤來確定版本號的,因此我們釋出版本時,需要給我們的倉庫打上一個標籤版本號。

也就是版本號 + 時間戳 +hash,我們自己指定版本時只需要制定版本號即可,沒有版本 tag 的則需要找到對應 commit 的時間和 hash 值。

還有一個重要的規則是,版本 0 和 1,最好需要有不同的依賴路徑,如:v1.0.0 和 v2.0.0 是有不同的依賴路徑,下面會詳細介紹一下這個版本規則。

釋出版本

瞭解了 go modules 的版本規則後,現在我們釋出一下該專案的版本:

$ git tag v1.0.0
$ git push --tags

這時我們最好還需要建立一條 v1 分支,以便我們在其它分支寫程式碼不會影響到 v1.0.0 版本:

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

使用 module

現在我們把剛剛做好的 module,拿過來用,新建一個 gomodules 專案:

package main

import (
    "fmt"
    "github.com/objcoding/testmod"
)

func main() {
    fmt.Println(testmod.SayHello("張乘輝"))
}

以前我們可以直接通過 go get 命令獲取依賴包,但是對於一個 module 專案來說,就遠遠比這個有趣多了,現將專案初始化成 module 專案:

$ go mod init

這時 go build 等命令就會下載依賴包,並把依賴資訊新增到 go.mod 檔案中,同時把依賴版本雜湊資訊存到 go.sum 檔案中:

$ go build

go: finding github.com/objcoding/testmod v1.0.0
go: downloading github.com/objcoding/testmod v1.0.0

這時,go.mod 檔案內容如下:

module gomodules
require github.com/objcoding/testmod v1.0.0

go.sum 檔案內容如下:

github.com/objcoding/testmod v1.0.0 h1:fGa15gBXoqkG0BVkQGP9i5Pg2nt8nayFpHFf+GLiX6A=
github.com/objcoding/testmod v1.0.0/go.mod h1:LGpYEmOLZhLQC3JW88STU2Ja3rsfoGZmsidsHJhDNGU=

這裡還需要注意的是,有時候我們引用 golang.org/x/ 的一些包,但發現在偉大的天朝這個地址是被qian了,但是我們程式設計師也充分發揮了勤奮好學的優良傳統,在 go modules 中設定了 goproxy 機制,如果 go modules 設定了代理,會優先從代理中下載依賴包,在 /etc/profile 中加入以下內容:

export GOPROXY="https://goproxy.io"

goproxy.io 谷歌官方的代理地址,當然還有很多國內優秀的第三方代理。

你也可以在 Goland編輯器中設定:

升級版本

現在我們來升級一下 testmod 專案:

$ cd gomodules
$ echo 'package testmod
		import "fmt"
	func SayHello(name string) string {
   return fmt.Sprintf("你好, %s", name)
}' >> testmod.go

我把「Hello」改成「你好」,我們把這個修改在 v1 分支中進行:

$ git commit -m "update testmod" testmod.go
$ git tag v1.0.1
$ git push --tags origin v1

現在我們的 專案已經升級到 v1.0.1 版本了,我們可以有多種方式獲取這個版本依賴,go1.11 中,go get 擁有了很多新特性,我們可以直接通過以下命令獲取 v1.01 版本依賴:

$ go get github.com/objcoding/[email protected]

也可以通過 go mod 命令:

$ go mod edit -require="github.com/objcoding/[email protected]"

$ go mod tidy

go mod edit -require 可以主動修改 go.md 檔案中依賴的版本號,然後通過 go mod tidy 對版本進行更新,這是一條神一樣的命令,它會自動清理掉不需要的依賴項,同時可以將依賴項更新到當前版本。

主要版本升級

上面版本規則說了,版本 0 和 1,即大版本更新,最好需要有不同的依賴路徑,如:v1.0.0 和 v2.0.0 是有不同的依賴路徑,那麼用 go modules 怎麼實現呢,我們可以通過修改 go.mod 檔案第一行新增新路徑:

$ cd testmod
$ echo 'module github.com/objcoding/testmod/v2' >> go.mod

然後我們修改 testmod 函式 Hi():

$ cd testmod

$ echo 'package testmod
import (
	"fmt"
)
func SayHello(name, str string) string {
	return fmt.Sprintf("你好, %s, %s", name, str)
}' >> testmod.go

這時,SayHello() 方法將不相容 v1 版本,我們需要新建一個 v2.0.0 版本,還是老樣子,我們最好在 v2.0.0 版本新建一條 v2 分分支,將 v2.0.0 版本的程式碼寫到這條分支中(這只是一個規範,實際上你將程式碼也寫到任何分支中都行,Go並沒有這個規範):

$ git add *
$ git checkout -b v2
$ git commit testmod.go -m "v2.0.0"
$ git tag v2.0.0
$ git push --tags origin v2

這時候 testmod 的版本已經更新到 v2.0.0 版本了,該版本並不相容以前的版本,但是目前專案依然只能獲取到 v1.0.1 的依賴版本,因為我們 testmod 的 module 路徑已經加上 v2 了,因此並不會出現衝突的問題,那麼如果我們需要使用 v2.0.0 版本呢?我們只需要在專案中更改一下 import 路徑:

package main

import (
    "fmt"
  	"github.com/objcoding/testmod/v2"
)
func main() {
    fmt.Println(testmod.SayHello("張乘輝", "最近過得怎樣"))
}

執行:

go mod tidy

這時我們把 testmod 依賴版本號更新到了 v2.0.0 版本了,雖然是此時的 import 路徑是以 “v2” 結尾,但是 Go 很人性化,我們依然可以使用 testmod 來使用。

公眾號「後端進階」,專注後   
 
 </div> 
 <div class=

相關推薦

Go Modules 總結

微信公眾號「後端進階」,專注後端技術分享:Java、Golang、WEB框架、分散式中介軟體、服務治理等等。 在 Java 的

用戶增長量統計項目實現過程總結

多進程、多線程、crontab 最近一段時間在做一個用戶訪問量統計的小項目,主要實現根據打包好的rdb文件進行解析、統計,然後存到pika,然後取到用戶key並統計訪問量的增長並將數據推到 grafana 進行展示。在代碼實現及部署過程中遇到了一些問題,總結出來,以備後續參考。歡迎批評指正,共同學習!一

信息統計DashBoard總結

mysql 字典 urllib 最近根據需求用Tornado框架在寫了一個從數據庫統計Redis服務器的數量、內存等信息的接口,並寫了一個從接口獲取數據並推送至grafana進行展示的程序。現在已經實現所有功能,現將實現過程中遇到的問題總結出來,以便後續參考。歡迎批評指正,共同學習進步!一

maven總結

常用命令 .com 是否 excludes enc cti prop equal schema maven踩坑 安裝 入門級的pom.xml配置 常用命令 maven插件 添加自己的源文件到maven中 使用外部依賴 測試文件的格式 其他內容,各位自取所需 mav

小白學習Spark系列四:rdd總結

build .text 大數據分析 遇到 ESS bstr 分隔符 讀取配置 關註   初次嘗試用 Spark+scala 完成項目的重構,由於兩者之前都沒接觸過,所以邊學邊用的過程大多艱難。首先面臨的是如何快速上手,然後是代碼調優、性能調優。本章主要記錄自己在項目中遇到的

WebViewGoogle和Chrome的總結

首先看一下谷歌的敘述 On Android 7.x where first api < 24, you should preinstall a regular APK of both Chrome and WebView, of the same version as each

openlayers5實戰--總結

1.介面返回圓心座標和半徑,直接通過new Circle(center,radius)新增圓形feature變小問題。   解決辦法:   new  Feature()的geometry引數不能直接賦值new  Circel()得到的geometry,   要通過‘ol/geom/P

微信小程式總結

1,滑動頁面的時候會出現留白。解決:最外層套一個scroll,不設定x、y滑動。注意距離要撐開,內層要有一個大的view,給它一個小margin-top 2,button出現多餘的邊框。解決:button::after設定border為none 3,自定義元件。自定義元件如果是page({}

Pandas.read_json()總結 & 原始碼初探

準備工作 環境依賴:Python 2.7 樣例資料(json檔案) 問題描述   通過Pandas.read_json(jsonFilePath)方法讀取json檔案時,會出現資料內容發生奇怪的轉變;Eg:假設樣例資料的檔名為data.json,

小程式總結

1,滑動頁面的時候會出現留白。解決:最外層套一個scroll,不設定x、y滑動。注意距離要撐開,內層要有一個大的view,給它一個小margin-top 2,button出現多餘的邊框。解決:button::after設定border為none

原生小程式轉mpvue總結

坑一:mpvue開發小程式時候,要新增靜態本地圖片 報錯: VM14878:2 Failed to load local image resource /images/bg.png  the server responded with a status of 40

聯想拯救者15-isk安裝win10+ubuntu16雙系統總結

1、一開始在efi模式下,系統無法識別u盤,在bios設定中將boot mode改為legacy support,此時可以識別了,但安裝系統後卻直接啟動了win10,因此要在efi模式下安裝ubuntu

積累與總結

1.微信快取 Andriod : debugx5.qq.com.   /    重新整理頁面多次 IOS: 重新整理頁面多次 2.403 nginx forbidden nginx 配置出錯 root /data/wwwroot/

如何使用sublime3愉快的編寫vue專案(總結

前段時間開始編寫一個vue專案,因為發現很多人使用sublime編輯器,所以我也選擇使用sublime,但是使用的過程並不順利,出現了一些問題,在網上查了很多資料,自己除錯了很多次總算弄好了,記錄下來希望可以幫到有需要的人。 1.sublime3的下載 sublime3的

vue + element-ui 製作tab切換(切換vue元件,總結

本篇文章使用vue結合element-ui開發tab切換vue的不同元件,每一個tab切換的都是一個新的元件。 1、vue如何使用element-ui 上一篇文章已經分享瞭如何在vue中使用element-ui建立tab元件切換內容(需要了解的朋友點選連結檢視) 2、建立相應檔案。   a、建立父元件

mpvue小程式以及微信直播總結

前段時間剛寫完一個mpvue的小程式,現在得閒必須趕緊記錄和總結一下,不然很多東西又要忘了 我是比較熟悉vue的語法,但是也猶豫過是用原生還是用mpvue,因為那時候原生小程式已經相當成熟而mpvue才剛出來(踩坑和填坑的前人還很少) 所以我學習了幾天原生的框架和語法,果

Golang語法的總結(持續更新中)

*本來這篇是想寫docker的基本操作總結的。。。想想還是寫這個吧。。。→_→* 變數宣告使用 := 方式時,左值必須是未宣告的,否則會出現編譯錯誤 //.\main.go:8:4: no

小程式實踐總結

六月份實在太忙,只能把遇到的問題簡單記錄,今天小程式二期的開發基本上結束了,才有空來總結整理在開發過程中遇到的問題。 在上一篇部落格小程式學習實踐總結中,我以為小程式的開發沒太多可說的,使用

antd table rowselection 總結

在 <Table/> 元件中有 rowSelection={rowSelection} 方法,可以讓Table的第一列成為聯動的選擇框。通過 rowSelection.selectedRowKeys 來控制選中項。 在需求中,有預設disabled的選項

ansible 總結

define 解決 stderr 1.2 就會 most 2.0 命令 host 12報錯12.1 libselinux-python aren’t installed[root@pythion ~]# yum -y install libselinux-python