查看原文
其他

用etcd做go-micro的服务发现

Go开发大全 2021-01-31

(给Go开发大全加星标)

来源:池塘边的卡夫卡

https://zhuanlan.zhihu.com/p/79897640

【导读】做微服务架构,微服务的治理统一框架其实非常有必要。本文介绍了如何使用golang和etcd实现服务发现。


在golang的生态中有众多框架,比如go-micro和go-kit还有华为开源的ServiceComb。


go-micro提供了一系列的组件,隐藏了分布式系统的复杂性,并为开发人员提供了很好的理解概念。开发者在开发的时候选择自己的组件就好了,如果系统不能支持,可以很方便的扩展。


在使用go-micro的服务发现组件的时候,由于我个人非常喜欢etcd。并且在一些分布式系统中可能会使用到分布式锁,所以服务发现的驱动我首选etcd。go-micro为etcd提供两组api一组是etcd一组是etcdv3。抱着用新不用久的思想果断选择etcdv3,这时候发现其实有一些坑。


按照官方文档我们的驱动选择etcd。代码如下

registerDrive := etcdv3.NewRegistry(func(op *registry.Options) { op.Addrs = []string{ "http://127.0.0.1:2379", }})
service := micro.NewService( micro.Transport(tg.NewTransport()), micro.Server(grpc.NewServer()), micro.Registry(registerDrive), micro.Broker(brokerDrive), micro.Name("userSrv"), micro.Version("latest"),)

然后我们启动etcd,启动etcd这时候发现报错。

../../../../github.com/coreos/etcd/clientv3/auth.go:121:72: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.AuthEnable../../../../github.com/coreos/etcd/clientv3/auth.go:126:74: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.AuthDisable../../../../github.com/coreos/etcd/clientv3/auth.go:131:152: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserAdd../../../../github.com/coreos/etcd/clientv3/auth.go:136:144: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserAdd../../../../github.com/coreos/etcd/clientv3/auth.go:141:86: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserDelete../../../../github.com/coreos/etcd/clientv3/auth.go:146:122: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserChangePassword../../../../github.com/coreos/etcd/clientv3/auth.go:151:104: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserGrantRole../../../../github.com/coreos/etcd/clientv3/auth.go:156:80: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserGet../../../../github.com/coreos/etcd/clientv3/auth.go:161:72: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserList../../../../github.com/coreos/etcd/clientv3/auth.go:166:106: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserRevokeRole../../../../github.com/coreos/etcd/clientv3/auth.go:166:106: too many errors

这时候我们谷歌一下,查查到底为啥报错。发现在etcd的github里面有一条issues。https://link.zhihu.com/?target=https%3A//github.com/etcd-io/etcd/issues/10051‍


这时候我们就清楚什么原因了,在go-micro里面引用的etcd的包是 github.com/coreos/etcd/而不是go.etcd.io/etcd/clientv这样一来可能就需要改源码了。


找到包github.com/micro/go-plu下发现三个文件一个个改掉就好

//watcher.goimport ( "context" "errors" "time"
//"github.com/coreos/etcd/clientv3" "go.etcd.io/etcd/clientv3" "github.com/micro/go-micro/registry")
//etcdv3.goimport ( "context" "crypto/tls" "encoding/json" "errors" "path" "strings" "sync" "time"
//"github.com/coreos/etcd/clientv3"
"go.etcd.io/etcd/clientv3" // 新加
"github.com/micro/go-micro/config/cmd" "github.com/micro/go-micro/registry"
//"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes" // 新加
hash "github.com/mitchellh/hashstructure")

改掉这些代码的时候发现etcd存进去的时候前缀我们顺便再机器上面watch一下

var ( prefix = "/micro-registry")
etcdctl watch "/micro-registry" --prefix

运行代码 看看我们watch监控的结果

PUT/micro-registry/userSrv/userSrv-4dfc1310-ba3e-4f76-a770-74ae5b4c074e{"name":"userSrv","version":"latest","metadata":null,"endpoints":[{"name":"User.Call","request":{"name":"Request","type":"Request","values":[{"name":"name","type":"string","values":null}]},"response":{"name":"Response","type":"Response","values":[{"name":"msg","type":"string","values":null}]},"metadata":{"stream":"false"}},{"name":"User.PingPong","request":{"name":"Context","type":"Context","values":null},"response":{"name":"Stream","type":"Stream","values":null},"metadata":{"stream":"true"}},{"name":"User.Stream","request":{"name":"Context","type":"Context","values":null},"response":{"name":"Stream","type":"Stream","values":null},"metadata":{"stream":"true"}}],"nodes":[{"id":"userSrv-4dfc1310-ba3e-4f76-a770-74ae5b4c074e","address":"10.0.2.15:38068","metadata":{"broker":"rabbitmq","registry":"etcdv3","server":"grpc","transport":"grpc"}}]}

停止程序

DELETE/micro-registry/userSrv/userSrv-4dfc1310-ba3e-4f76-a770-74ae5b4c074e

这样一来我们就可以使用etcd做服务发现了,其实关于这一块的代码也很简单,甚至你自己也可以去实现。具体可以参照他的代码,有空甚至你可以使用redis的发布订阅或者写一个基于redis驱动的练练手。

// 需要实现的接口如下type Registry interface { Register(*Service, ...RegisterOption) error Deregister(*Service) error GetService(string) ([]*Service, error) ListServices() ([]*Service, error) Watch(...WatchOption) (Watcher, error) String() string Options() Options}

另外需要删除etcd包下面的vendor文件,不然也会报错。


其他

  1. 上述的容器都不是goroutines 安全的

  2. 上面的lr 也不是goroutines 安全的

  3. Ring 中不建议在Do 方法中修改Ring 的指针,行为是未定义的


 - EOF -

推荐阅读(点击标题可打开)

1、k8s调度的优先级及抢占机制源码分析

2、Golang 的 sync.Pool设计思路与原理

3、Golang异常处理panic、recover机制详解

如果觉得本文不错,欢迎转发推荐给更多人。


分享、点赞和在看

支持我们分享更多好文章,谢谢!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存