共计 5851 个字符,预计需要花费 15 分钟才能阅读完成。
作者 | 董剑辉、盛傲飞
起源 | 阿里巴巴云原生公众号
问题背景
该问题源于咱们想对 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 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 在跑集成测试时产生了谬误,具体的谬误提示信息如下:
这一段的执行逻辑是心愿利用 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
拉包时产生了谬误。
回过头查看谬误日志,咱们能够发现:
因为咱们只指定了 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 即可,咱们在 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,胜利拉取了依赖。
问题拓展
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 多模块的开发模式。
- 某个嵌套模块变动十分频繁,须要常常发版。
- 当中的嵌套模块仅仅依赖某个固定版本的根模块。
两种状况其实实质都是两个模块之间没什么强的版本绑定关系,然而因为一些其余起因须要放在一个 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。