天天看點

dubbo-go v3 版本 go module 踩坑記問題背景問題分析問題解決問題拓展

dubbo-go v3 版本 go module 踩坑記問題背景問題分析問題解決問題拓展

作者 | 董劍輝、盛傲飛

來源 | 阿裡巴巴雲原生公衆号

問題背景

該問題源于我們想對 dubbo-go 的 module path 做一次變更,使用

dubbo.apache.org/dubbo-go/v3

替換之前的

github.com/apache/dubbo-go

首先,我們做了路徑映射,在 dubbo.apache.org 下放置了一個 dubbogo/v3 檔案,内容如下:

<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 rel="nofollow" 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 路徑。

問題分析

在做完上述的修改後,我們提 PR 時,發現 CI 失敗,經過進一步的日志排查,我們确定是 CI 在跑內建測試時發生了錯誤,具體的錯誤提示資訊如下:

dubbo-go v3 版本 go module 踩坑記問題背景問題分析問題解決問題拓展

這一段的執行邏輯是希望利用 docker 對 dubbo-go 中的內建測試内容建構鏡像,并啟動容器進行測試,該鏡像打包所用的 Dockerfile 路徑在

github.com/apache/dubbo-go/test/integrate/dubbo/go-server

目錄下,對照錯誤日志的 STEP 辨別,我們可以定位到具體錯誤發生下面的兩個步驟:

# ...

# 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

拉包時發生了錯誤。

回過頭檢視錯誤日志,我們可以發現:

dubbo-go v3 版本 go module 踩坑記問題背景問題分析問題解決問題拓展

由于我們隻指定了 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 依賴的時候出錯。

問題解決

在問題分析一節中我們定位到這個問題在鏡像建構的 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 即可,我們在 g

o 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,成功拉取了依賴。

dubbo-go v3 版本 go module 踩坑記問題背景問題分析問題解決問題拓展

問題拓展

1. Semantic Import Versioning

在我們使用 Go modules 去建構項目的依賴關系時,對 go 項目的依賴都需要聲明我們所使用的版本号,考慮到每次釋出新版本時,相容一直是開發者最為重視和頭痛的問題,是以 go 通過語義導入版本控制(Semantic Import Versioning)來制定版本号的标準,來確定每個開發者能夠根據自己的項目需求指定使用的依賴版本。根據 go 的語義導入版本控制準則,版本号由三部分構成:

dubbo-go v3 版本 go module 踩坑記問題背景問題分析問題解決問題拓展
注:上圖及以上部分内容參考自《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

的依賴:

dubbo-go v3 版本 go module 踩坑記問題背景問題分析問題解決問題拓展

但考慮到在某些場景下,我們想要使用子產品依賴的某個未發版的版本(該子產品隻有一個已知的 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 rel="nofollow" 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

    :使用的版本控制工具。
  • &lt;https://github.com/apache/dubbo-go&gt;

    :告訴 go get 這個項目應該去哪兒找源代碼。

2)go-source

go-source

的作用,則是給項目生成具體的

go doc

(現為 pkg.go.dev ) 文檔,一共有 4 部分,前兩部分和

go-import

一樣,是該項目的 module 聲明和版本控制工具,後兩部分則分别起如下作用:

  • &lt;https://github.com/apache/dubbo-go/tree/3.0{/dir}&gt;

    :聲明該項目的源代碼所在位置。
  • &lt;https://github.com/apache/dubbo-go/blob/3.0{/dir}/{file}#L{line}&gt;

    :映射文檔和代碼,幫助我們在點選文檔的目錄樹時,可以跳轉到對應的具體内容。

比如在

https://pkg.go.dev/dubbo.apache.org/dubbo-go/v3

上,我們點選其中一個檔案:

dubbo-go v3 版本 go module 踩坑記問題背景問題分析問題解決問題拓展

參考資料

  • 《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/

作者簡介