后端技术总结

  • 首页
  • github
  • About Me
  1. 首页
  2. ETCD
  3. 正文

记踩Etcd的一个坑

2021年4月21日 2760点热度 5人点赞 0条评论
内容纲要

背景

最近一个服务引入了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版本中解决,大家拭目以待吧, 毕竟, 你们已经都等了几个月了,不在乎再多等些日子。
那我们就等等看吧。
谨以此记录下这个问题,防止忘掉。

标签: etcd golang
最后更新:2021年8月15日

PingD

反思让我成长

点赞
< 上一篇
下一篇 >

文章评论

取消回复

PingD

反思让我成长

归档
  • 2023年1月
  • 2022年3月
  • 2022年1月
  • 2021年10月
  • 2021年8月
  • 2021年4月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2019年9月
  • 2019年7月
  • 2019年6月
  • 2019年4月
  • 2019年3月
最新 热点 随机
最新 热点 随机
断更了,不想写了 新路由2(newifi d1) 小白刷老毛子固件 2021年度总结 golang之从源码角度看slice的len和cap 基于chan实现的简易协程池及说明 订单超时方案(初稿)
Go-如何将任意int或float转成string 记踩Etcd的一个坑 golang之从源码角度看slice的len和cap golang 判断interface是否为空nil Expect脚本之一键登录 Golang之HTTP EOF/connection reset by peer详解
最近评论

COPYRIGHT © 2021 dpjeep.com. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS

蜀ICP备18036663号-1