dubbo-go v3 版本 go module 踩坑記

簡介: 該問題源於咱們想對 dubbo-go 的 module path 作一次變動,使用 dubbo.apache.org/dubbo-go/v3 替換以前的 github.com/apache/dubbo-go。html

做者 | 董劍輝、盛傲飛
來源 | 阿里巴巴雲原生公衆號
git

問題背景


該問題源於咱們想對 dubbo-go 的 module path 作一次變動,使用 dubbo.apache.org/dubbo-go/v3 替換以前的 github.com/apache/dubbo-go。

首先,咱們作了路徑映射,在 dubbo.apache.org 下放置了一個 dubbogo/v3 文件,內容以下:
github

<html>
  <head>
    <meta name="go-import" content="dubbo.apache.org/dubbo-go/v3 git <https://github.com/apache/dubbo-go>">
    <meta name="go-source" content="dubbo.apache.org/dubbo-go/v3 git <https://github.com/apache/dubbo-go/tree/3.0{/dir}> <https://github.com/apache/dubbo-go/blob/3.0{/dir}/{file}#L{line}>">
    <meta http-equiv="refresh" content="0; url=https://pkg.go.dev/dubbo.apache.org/dubbo-go/v3">
  </head>
  <body>
    <p>Redirecting to <a href="<https://pkg.go.dev/dubbo.apache.org/dubbo-go/v3>">pkg.go.dev/dubbo.apache.org/dubbo-go/v3</a>...</p>
  </body>
</html>


其次,咱們修改了 go.mod 的 module 和對應的全部 import,並修改了全部子模塊引用 dubbo-go 使用的 module 路徑。
golang

問題分析


在作完上述的修改後,咱們提 PR 時,發現 CI 失敗,通過進一步的日誌排查,咱們肯定是 CI 在跑集成測試時發生了錯誤,具體的錯誤提示信息以下:
docker

這一段的執行邏輯是但願利用 docker 對 dubbo-go 中的集成測試內容構建鏡像,並啓動容器進行測試,該鏡像打包所用的 Dockerfile 路徑在 github.com/apache/dubbo-go/test/integrate/dubbo/go-server 目錄下,對照錯誤日誌的 STEP 標識,咱們能夠定位到具體錯誤發生下面的兩個步驟:
apache

# ...

# STEP 9
RUN test ${PR_ORIGIN_REPO} && go mod edit -replace=dubbo.apache.org/dubbo-go/v3=github.com/{PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID} || go get -u dubbo.apache.org/dubbo-go/v3@develop


# ...


# STEP 11
RUN go mod tidy && go install github.com/apache/dubbo-go/test/integrate/dubbo/go-server


在 STEP 9 中,咱們使用 go mod edit -replace 替換了 dubbogo 的依賴路徑,將其替換爲發起 PR 請求的倉庫地址和 commit id。在此基礎上,當鏡像構建跑到 STEP11 ,嘗試使用 go mod tidy 拉包時發生了錯誤。

回過頭查看錯誤日誌,咱們能夠發現:
工具

因爲咱們只指定了 commit id,所以在 go mod 實際運行過程當中,它會爲咱們生成一個假定版本號(關於假定版本號的更多說明能夠查看本文最後的問題拓展),這個假定版本號抓取遠程倉庫的最新有效 tag 爲 v1.4.1【注:此處遠程倉庫爲 github.com/Mulavar/dubbo-go,這是我本身的 dubbo-go 分支,該分支拉取 fork 的時間比較早,其最後 tag 是 v1.4.1】,並自增爲 v1.4.2,主版本是 v1。但在先前,咱們的 dubbogo module path 是聲明爲 dubbo.apache.org/dubbogo/v3,主版本是 v3,這致使了 go mod edit -replace 先後的依賴主版本分別是 v3 和 v1 ,出現了不一致,實際拉取 dubbogo 依賴的時候出錯。
post

問題解決


在問題分析一節中咱們定位到這個問題在鏡像構建的 STEP 9,即:
測試

# STEP 9
RUN test ${PR_ORIGIN_REPO} && go mod edit -replace=dubbo.apache.org/dubbo-go/v3=github.com/{PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID} || go get -u dubbo.apache.org/dubbo-go/v3@develop


這一步,咱們使用 go mod edit -replace 時將一個 v3 版本的 go module 替換成了一個 v1 版本的 module,致使主版本不一致發生錯誤,所以咱們只需在替換依賴路徑時指定替換後的 module 主版本也爲 v3 便可,咱們在 go mod edit -replace 後的模塊依賴路徑後也加上 v3 後綴以下所示。

修改前:

go mod edit -replace=dubbo.apache.org/dubbo-go/v3=github.com/{PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID}

⇒ 修改後:

go mod edit -replace=dubbo.apache.org/dubbo-go/v3=github.com/${PR_ORIGIN_REPO}/v3@${PR_ORIGIN_COMMITID}

修復該問題後咱們提交代碼查看 CI,在 STEP 8 打印的日誌信息中,能夠看到替換後的 dubbogo 路徑多了 v3,而且在拉包的時候跟的版本號(v3.0.0-20210509140455-2574eab5ad0b)主版本一樣是 v3,成功拉取了依賴。ui

問題拓展

1. Semantic Import Versioning


在咱們使用 Go modules 去構建項目的依賴關係時,對 go 項目的依賴都須要聲明咱們所使用的版本號,考慮到每次發佈新版本時,兼容一直是開發者最爲重視和頭痛的問題,所以 go 經過語義導入版本控制(Semantic Import Versioning)來制定版本號的標準,來確保每一個開發者可以根據本身的項目需求指定使用的依賴版本。根據 go 的語義導入版本控制準則,版本號由三部分構成:

注:上圖及以上部份內容參考自《Go Modules 詳解》一文。

其中 Major version 表示這是一個新的大版本,甚至這個新版本可能和舊版本是不兼容的。

而 Minor version 則表示這是一個大版本中的迭代,主要用於新增 feature 時遞增。

Patch version 則是粒度最細的版本更新,表示一些 bug 的修復。

而指定主版本則有兩種方式,一種是如上的直接聲明,另外一種則是在項目路徑的最後加上版本後綴,如 dubbogo 3.0 版本聲明的 module 則是 dubbo.apache.org/dubbo-go/v3,其中 v3 就是一個主版本的聲明。

2. pseudo-version


Go 在使用依賴時推崇咱們指定明確的版本號,好比 dubbogo 的 go.mod 中聲明的對 cloud.google.com/go 的依賴:

但考慮到在某些場景下,咱們想要使用模塊依賴的某個未發版的版本(該模塊只有一個已知的 commit id),如 dubbogo 聲明的 github.com/Microsoft/go-winio 依賴,就可使用一個假定版本號(pseudo-version)去替代真實的版本號。假定版本號的格式爲:

// (1) vX.0.0-yyyymmddhhmmss-abcdef123456
// (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456
// (3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible
// (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456
// (5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible


能夠看到 pseudo-version 被短斜槓分爲三部分,其中第一部分 X.Y.Z 與 4.1 節提到的一致,分別是 major version、minor version、patch version,而第二部分是一個格式爲 yyyymmddhhmmss 的時間戳,第三部分是一個 12 位的 commit hash,能夠手動指定,若是沒有,則由 go 自動生成。

關於 pseudo-version,go 的生成規則以下:

  • 查詢該項目對應主版本(若項目依賴路徑沒有顯式的 v2/v3 後綴,則默認爲 v1 版本)的最新 tag。
  • 有 tag 則使用最新 tag 遞增 patch version。
  • 無 tag ,根據項目路徑帶的後綴版本號自動生成(如 v3 則自動生成一個 3.0.0 開頭的 pseudo-version)。

遵循這個規則,回頭看前文的問題分析和問題解決,咱們就能夠明白爲何在一開始 dubbo-go 的 pseudo-version 是 v1.4.2,這正是由於 go 認爲 replace 後的 dubbogo 依賴主版本是 v1,去查找了該主版本下的最新 tag,並遞增了 patch version,從而致使先後主版本不一致,當咱們對版本路徑加上 v3 後,go 查找不到對應主版本下的 tag,爲咱們自動生成了 v3.0.0,從而經過了 CI 。

3. Go 模塊嵌套


和 Java 不一樣,go 實際上是沒有子模塊概念的。即便有些時候,咱們會看到有個 repo 裏有多個 Go modules,好比項目的根目錄有一個 go.mod ,裏面有些子目錄裏又有 go.mod 。

這在 Go modules 被稱爲嵌套模塊,而非父子模塊,即兩個模塊沒有任何關係,是相互獨立的

那何時才須要單 repo 多模塊呢?通常來講,碰到如下兩種狀況咱們纔會考慮使用單 repo 多模塊的開發形式。

  1. 某個嵌套模塊變更很是頻繁,須要常常發版。
  2. 當中的嵌套模塊僅僅依賴某個固定版本的根模塊。

兩種狀況其實本質都是兩個模塊之間沒什麼強的版本綁定關係,可是因爲一些其餘緣由須要放在一個 rpeo 下,所以造成了單 repo 多模塊的局面。

4. dubbogo 靜態映射文件內容解析


dubbo-go 使用了靜態文件映射的方式實現了模塊重定向,靜態文件的內容以下:

其中的核心部分是 meta 標籤 go-import 和 go-source。

<html>
  <head>
    <meta name="go-import" content="dubbo.apache.org/dubbo-go/v3 git <https://github.com/apache/dubbo-go>">
    <meta name="go-source" content="dubbo.apache.org/dubbo-go/v3 git <https://github.com/apache/dubbo-go/tree/3.0{/dir}> <https://github.com/apache/dubbo-go/blob/3.0{/dir}/{file}#L{line}>">
    <meta http-equiv="refresh" content="0; url=https://pkg.go.dev/dubbo.apache.org/dubbo-go/v3">
  </head>
  <body>
    <p>Redirecting to <a href="<https://pkg.go.dev/dubbo.apache.org/dubbo-go/v3>">pkg.go.dev/dubbo.apache.org/dubbo-go/v3</a>...</p>
  </body>
</html>

1)go-import


go-import 的做用,是告訴 go get 去哪兒能夠找到源碼,content 分爲三部分:

  • dubbo.apache.org/dubbo-go/v3:這個項目的 module 聲明。
  • git:使用的版本控制工具。
  • <https://github.com/apache/dubbo-go>:告訴 go get 這個項目應該去哪兒找源代碼。

2)go-source


go-source 的做用,則是給項目生成具體的 go doc(現爲 pkg.go.dev ) 文檔,一共有 4 部分,前兩部分和 go-import 同樣,是該項目的 module 聲明和版本控制工具,後兩部分則分別起以下做用:

  • <https://github.com/apache/dubbo-go/tree/3.0{/dir}>:聲明該項目的源代碼所在位置。
  • <https://github.com/apache/dubbo-go/blob/3.0{/dir}/{file}#L{line}>:映射文檔和代碼,幫助咱們在點擊文檔的目錄樹時,能夠跳轉到對應的具體內容。

好比在 https://pkg.go.dev/dubbo.apache.org/dubbo-go/v3 上,咱們點擊其中一個文件:

就會跳轉到對應文件對應的文檔。

歡迎對 apache/dubbo-go 項目有興趣的同窗搜索釘釘羣號 31363295,加入釘釘交流羣!

參考資料

  • 《The Go Blog — Using Go Modules》:https://blog.golang.org/using-go-modules
  • 《Go Modules Reference》:https://golang.org/ref/mod
  • 《Go Module 如何發佈 v2 及以上版本》:https://blog.cyeam.com/go/2019/03/12/go-version
  • 《Go Modules 詳解》:https://www.sulinehk.com/post/go-modules-details/

做者簡介


董劍輝(github @Mulavar),剛從 浙江大學 VLIS 實驗室畢業,目前在任 猿輔導 服務端開發工程師。

盛傲飛(github @aofei),goproxy.cn 項目做者,曾給 go 源碼【如 mod 工具】提交過不少 PR。

 

本文爲阿里雲原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索