首先,注意区分使用Go mod后,module和package的概念
- module是go mod的引入单位,go.mod文件里引入的是module:
- package是go代码import的单位,一个module可以覆盖多个package
Go mod 嵌套引用不同版本的module,最终使用哪个版本?
答案是:全局使用二者之中最新的版本来运行程序。
比如下面的目录关系:
$ tree
.
├── go.mod
├── go.sum
├── mtest
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── test.go
在test.go
一级的go.mod
里引用一个版本的qmgo
:
require (
github.com/qiniu/qmgo v0.7.6
)
在mtest
一级的go.mod
里使用另一个版本:
require (
github.com/qiniu/qmgo v0.7.7
)
然后在test.go
里调用mtest
包里的方法RunTest()
,此时,go mod
会选择二者中最新的版本来进行编译和运行,也是就说test.go
和mtest.go
里使用的qmgo
都是v0.7.7版本(test.go
一级的go.mod
里的v0.7.6会被改写成v0.7.7)。
上例中两个go.mod的引用版本调换,结果依然是使用最新的版本来编译和运行。
Go mod的这个设计无可厚非,只是实际工程中,包的引用是间接的、复杂的,如果某个包的新版本不向前对旧版本兼容,比如新版本删除了一个旧版本的API,那么使用新版本来编译旧版本实现,会导致编译失败。解决方案需要根据实际情况来具体分析,比如替换旧版本module引用(线上更新或者拉私有库修改),回退新版本,某些情况下也可以通过下面的方法同时支持2个版本的存在。
代码如下:
test.go:
package main
import (
"context"
"fmt"
"mtest" // test.go一层的go.mod里,mtest需要replace,上面的go.mod里没有写
"github.com/qiniu/qmgo"
)
func main() {
client, err := qmgo.NewClient(context.Background(), &qmgo.Config{Uri: "mongodb://localhost:27017"})
fmt.Println(err)
defer client.Close(context.Background())
mtest.RunTest()
}
mtest.go:
package mtest
import (
"context"
"fmt"
"github.com/qiniu/qmgo"
)
func RunTest() {
client, err := qmgo.NewClient(context.Background(), &qmgo.Config{Uri: "mongodb://localhost:27017"})
fmt.Println(err)
defer client.Close(context.Background())
}
Go mod如何同时引用多个版本的module
使用replace即可达成目的
module test
go 1.14
replace github.com/qiniu/qmgo077 => github.com/qiniu/qmgo v0.7.7
require (
github.com/qiniu/qmgo v0.7.6
github.com/qiniu/qmgo077 v0.0.0-00010101000000-000000000000 // 自动生成
)
import (
"context"
"fmt"
"github.com/qiniu/qmgo"
qmgo077 "github.com/qiniu/qmgo077"
)
func main() {
client, err := qmgo.NewClient(context.Background(), &qmgo.Config{Uri: "mongodb://localhost:27017"})
fmt.Println(err)
defer client.Close(context.Background())
client077, err := qmgo077.NewClient(context.Background(), &qmgo077.Config{Uri: "mongodb://localhost:27017"})
fmt.Println(err)
defer client077.Close(context.Background())
}