背景
最近一个服务引入了etcd,用于主从,编译能够正常通过,但是在run阶段出现如下panic:
2020-12-14 13:36:42.799342 I | WARNING: proto: message authpb.User is already registeredA future release will panic on registration conflicts. See:https://developers.google.com/protocol-buffers/docs/reference/go/faq
#namespace-conflict2020-12-14 13:36:42.799365 I | WARNING: proto: message authpb.Permission is already registeredA future release will panic on registration conflicts. See:https://developers.google.com/protocol-buffers/docs/reference/go/faq
#namespace-conflict2020-12-14 13:36:42.799373 I | WARNING: proto: message authpb.Role is already registeredA future release will panic on registration conflicts. See:https://developers.google.com/protocol-buffers/docs/reference/go/faq
#namespace-conflictpanic: proto: duplicate enum registered: authpb.Permission_Typegoroutine 1
[running]:github.com/golang/protobuf/proto.RegisterEnum(0x70a4aac, 0x16, 0xc0005c4f30, 0xc0005c4f60)
/Users/pingd/Go/pkg/mod/github.com/golang/protobuf@v1.4.2/proto/registry.go:104 +0x11fgo.etcd.io/etcd/auth/authpb.init.0()
/Users/pingd/Go/pkg/mod/go.etcd.io/etcd@v0.5.0-alpha.5.0.20200821141407-46a0a44f9539/auth/authpb/auth.pb.go:115 +0x10a
通过上面提示,大致意思是:服务在启动时,有枚举出现了重复注册的情况,通过给出的链接往下跳,此次panic在这里:
// https://github.com/etcd-io/etcd/blob/v3.3.10/auth/authpb/auth.pb.go#L103
func init() {
proto.RegisterType((*User)(nil), "authpb.User")
proto.RegisterType((*Permission)(nil), "authpb.Permission")
proto.RegisterType((*Role)(nil), "authpb.Role")
proto.RegisterEnum("authpb.Permission_Type", Permission_Type_name, Permission_Type_value)
}
寻找原因
通过上面的一些信息已经能够初步给出一个panic的结论:protobuf里面的注册事件引发了这个报错。但是为何会出现这种情况呢?有点想不明白了,因为引入etcd来进行选主不是这个服务独有的,其它服务早就存在了,有点开始抓脑壳了。
首先使用go mod why
命令,查一下新引入的etcd报是被谁在用:
$ go mod why github.com/coreos/etcd
# github.com/coreos/etcd(main module does not need package github.com/coreos/etcd)
没找到,咱们继续使用go mod graph
命令详细的查看一下,但是因为这个命令会直接把所有依赖包都打印出来,如果项目依赖的包比较少的话还好,如果很多的话就有点大海捞针了。于是我们使用grep来过滤掉不需要的信息,既然这次是因为引入了新的github.com/coreos/etcd
包引起的,那就首先看看这次引入的包的情况吧:
$ go mod graph|grep " github.com/coreos/etcd"
my-server github.com/coreos/etcd@v3.3.10+incompatible // my-server 是我们这次的服务名,为防敏感做了特殊处理
github.com/spf13/viper@v1.3.2
github.com/coreos/etcd@v3.3.10+incompatible
github.com/spf13/viper@v1.4.0
github.com/coreos/etcd@v3.3.10+incompatible
发现除了my-server本身调用外,也在被github.com/spf13/viper
调用,那github.com/spf13/viper
又在被哪个包调用呢?我们继续查找
$ go mod graph|grep " github.com/spf13/viper"
github.com/spf13/cobra@v0.0.5
github.com/spf13/viper@v1.3.2
github.com/spf13/cobra@v1.0.0
github.com/spf13/viper@v1.4.0
就这样一步一步的查找,过滤掉一些无用的包,大致出现了以下的依赖图关系:
看到上面的图,凭直觉就感觉有些不对头的地方了,同是etcd,为何是两个包的数据源呢?
github.com/coreos/etcd // 这个是我新引入的
go.etcd.io/etcd // 这个是pingcap依赖的
其实问题就是出在这个上面,虽然是两个地址,但是后面代码都是一样的,在两个不同的组件引用了这两个不同的数包的时候,他们内部都触发了proto.buf
的初始化,所以出现了最开始的那个报错。etcd
最开始是共同存放在github.com/coreos
下的,后面因为etcd发展的太好,就准备独立出来,可是这时因为项目已经名声在外,不能随便的更换域名,所以你会发现在github
上面和新的go.etcd.io
上面同时在维护,我们从github
上面的main
函数里面的代码也能看到:
链接:https://github.com/etcd-io/etcd/blob/ae7862e8bc8007eb396099db4e0e04ac026c8df5/server/main.go#L23
// Package main is a simple wrapper of the real etcd entrypoint package
// (located at go.etcd.io/etcd/etcdmain) to ensure that etcd is still
// "go getable"; e.g. `go get go.etcd.io/etcd` works as expected and
// builds a binary in $GOBIN/etcd
// This package should NOT be extended or modified in any way; to modify the
// etcd binary, work in the `go.etcd.io/etcd/etcdmain` package.
package main
import (
"os"
"go.etcd.io/etcd/server/v3/etcdmain"
)
func main() {
etcdmain.Main(os.Args)
}
解决方案
找到上面的原因后再来处理就比较快速了,直接把我们新引入的包由github.com/coreos/etcd
替换成go.etcd.io/etcd
就好了(主要是同源问题),比如
<!--more-->
"github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/mvcc/mvccpb"
替换为
"go.etcd.io/etcd/clientv3"
"go.etcd.io/etcd/mvcc/mvccpb"
小结
在使用etcd的过程中坑算是比较多的了,鸟哥曾也说过这个问题,其中也有很多是etcd本身的锅,很多人早就在抱怨了,对go module的支持貌似不是很积极,不过据鸟哥说:据说,这个问题会在 etcd v3.5.0版本中解决,大家拭目以待吧, 毕竟, 你们已经都等了几个月了,不在乎再多等些日子。
那我们就等等看吧。
谨以此记录下这个问题,防止忘掉。
文章评论