我是从18年开始接触golang这门语言的,在当时,广泛应用于包管理的,是vendor.在项目开发的时候,我们只要在项目的根目录下,执行一下vendor初始化(govendor init),那么这个项目根目录下就会生产用于包管理的文件目录vendor/.该目录下有一个vendor.json文件,这个文件类似 godep 工具中的描述文件版本的功能。
下面是一个这个文件的示例
{
"comment": "",
"ignore": "",
"package": [
{
"checksumSHA1": "qL4naKgOOJE2hXxKKYu+5W2ULHU=",
"path": "github.com/certifi/gocertifi",
"revision": "0944d244cd40db6fa14fa14fc8c53b29f0ad66e4",
"revisionTime": "2019-10-21T19:10:39Z"
},
......
],
"rootPath": "/path/to/project"
}
当然了,在项目创建之初,还未导入软件包的时候,是没有package列表的内容的。当我们在项目开发过程中,引进了软件包之后,只要执行一下govendor add +external命令,就会将引用的软件包,加入到package列表中。
除此之外,还会将当前版本的软件包,放入到vendor目录中,详细可以看一下下方的示例
我们在项目代码管理系统中,需要把vendor文件进行统一管理。
最近在新的项目中,引入了从golang 1.11 1.12版本中开始引进的包管理工具mod.
这个和vendor还是不太一样的。我们接下来看看mod是如何管理软件包的。
模块是存储在文件树中的Go软件包的集合,其根目录中有go.mod文件。 go.mod文件定义了模块的模块路径(这也是用于根目录的导入路径)及其依赖项要求,它们是成功构建所需的其他模块。 每个依赖性要求都写为模块路径和特定的语义版本。
通过一个简单的示例来看一下
我们将micro中的greeter示例,拿出来,写一个简单的客户端服务器模式的hello world.
首先看一下greeter.proto文件
syntax = "proto3";
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string greeting = 2;
}
整个项目的功能可谓是非常简单来,那我们来看看如何通过mod来进行包管理
首先,我们来通过go mod init /path/to/project来进行初始化操作,这个操作,会在项目的根目录下生产一个go.mod文件
module modtest
go 1.13
我们通过protoc生产相应的go代码.
protoc --micro_out=. --go_out=. ./greeter/greeter.proto
然后实现一个简单的服务器
➜ server more server.go
package main
import (
"context"
"fmt"
proto "modtest/greeter"
micro "github.com/micro/go-micro"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
func main() {
// Create a new service. Optionally include some options here.
service := micro.NewService(
micro.Name("greeter"),
)
// Init will parse the command line flags.
service.Init()
// Register handler
proto.RegisterGreeterHandler(service.Server(), new(Greeter))
// Run the server
if err := service.Run(); err != nil {
fmt.Println(err)
}
}
将服务器运行起来,之后,实现一个客户端来跟服务器进行通讯
➜ client more client.go
package main
import (
"context"
"fmt"
proto "modtest/greeter"
micro "github.com/micro/go-micro"
)
func main() {
// Create a new service. Optionally include some options here.
service := micro.NewService(micro.Name("greeter.client"))
service.Init()
// Create new greeter client
greeter := proto.NewGreeterService("greeter", service.Client())
// Call the greeter
rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{Name: "John"})
if err != nil {
fmt.Println(err)
}
// Print response
fmt.Println(rsp.Greeting)
}
然后我们现在来看看其依赖包的管理吧
➜ modtest more go.mod
module modtest
go 1.13
require github.com/micro/go-micro v1.18.0
自动引入了go-micro包。并且指定了版本。同时,你会发现,项目目录下,多了一个文件go.sum。
截取部分内容看一下
➜ modtest more go.sum
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw=
这些都是软件版本的加密哈希。确保以后这些模块的下载与第一次下载时检索的位相同,以确保您的项目所依赖的模块不会由于恶意,意外或其他原因而意外更改。
应该将go.mod和go.sum都提交到代码版本控制中。
那如果我们升级依赖该怎么办呢?
$ go list -m all
github.com/coreos/bbolt v1.3.3
github.com/coreos/etcd v3.3.17+incompatible
github.com/coreos/go-semver v0.3.0
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f
发现么,有的直接指定了其特定版本,有的直接给的v0.0.0
一个给定的版本分为三个部分:主体,小版本,补丁,比如v1.3.3,主要版本为1,小版本为3,补丁版本为3.而这种v0.0.0是未标记的版本。也就是说,我们升级这种版本为最新版本也没有影响,项目一样可以正常运行。
➜ modtest go get github.com/coreos/pkg
go: finding github.com/coreos/pkg latest
运行一下我们的项目
go run server.go
报错
➜ server go run server.go
# github.com/coreos/etcd/clientv3/balancer/resolver/endpoint
../../../../pkg/mod/github.com/coreos/etcd@v3.3.17+incompatible/clientv3/balancer/resolver/endpoint/endpoint.go:114:78: undefined: resolver.BuildOption
../../../../pkg/mod/github.com/coreos/etcd@v3.3.17+incompatible/clientv3/balancer/resolver/endpoint/endpoint.go:182:31: undefined: resolver.ResolveNowOption
# github.com/coreos/etcd/clientv3/balancer/picker
../../../../pkg/mod/github.com/coreos/etcd@v3.3.17+incompatible/clientv3/balancer/picker/err.go:37:44: undefined: balancer.PickOptions
../../../../pkg/mod/github.com/coreos/etcd@v3.3.17+incompatible/clientv3/balancer/picker/roundrobin_balanced.go:55:54: undefined: balancer.PickOptions
查看一下我们的go.mod
➜ modtest more go.mod
module modtest
go 1.13
require (
github.com/golang/protobuf v1.4.1
github.com/micro/go-micro v1.18.0
google.golang.org/protobuf v1.25.0
)
软件版本不兼容。如何解决这样的问题呢,我们想看错误的信息中有什么比较有价值的东西,确实大家都能看出来是不兼容,谁和谁不兼容,如何解决不兼容。
我们先来看看etcd这个代码库的3.3.17这个版本其关联的代码库版本,能和我们go.mod管理的软件包有管理的也就是google.golang.org/protobuf了,我们的go.mod中版本是v1.25.0.
而etcd 3.3.17所需要的版本是v1.23.0版本的grpc。在看v1.23.0版本的grpc
所以解决此类冲突的版本要么升级etcd要么降级protobuf.考虑到易操作性和符合思考方式的解决方案就是直接更新protobuf的版本。
将go.mod中的protobuf版本更新为v1.23.0之后,启动服务器成功
module modtest
go 1.13
require (
github.com/golang/protobuf v1.4.1
github.com/micro/go-micro v1.18.0
google.golang.org/protobuf v1.23.0
)
最后,可能由于在调试过程中,引入了很多不必要的版本。导致go.mod比较混乱,这个时候可以通过
$go mod tidy
进行更新包的管理。
针对mod方式的软件包依赖管理和vendor,大家有什么看法,更喜欢哪个?欢迎大家一起讨论