记一次服务注册&服务发现的问题和解决(etcd和k8s同时结合使用情况下)
目录背景分析原因总结背景早先的都采用基于grpc+etcd做服务注册和服务发现,都是正常的后来有部分服务采用k8s部署,为了新旧兼容,服务会按照老方式把宿主机的ip注册到etcd上,k8s体系类使用体系类的服务发现,k8s体系外的依然使用原来的方式,互不影响但是有服务基于k8s部署之后,发现client调用接口超时,而直接通过ip调用却是正常且多数是发生在...
·
目录
背景
- 早先的都采用基于grpc+etcd做服务注册和服务发现,都是正常的
- 后来有部分服务采用k8s部署,为了新旧兼容,服务会按照老方式把宿主机的ip注册到etcd上,k8s体系类使用体系类的服务发现,k8s体系外的依然使用原来的方式,互不影响
- 但是有服务基于k8s部署之后,发现client调用接口超时,而直接通过ip调用却是正常
- 且多数是发生在k8s的出现pod弹性伸缩之后
分析
- 服务端注册地址,从etcd来看,是正常的
- 服务端直接通过ip调用也正常的,所以排除服务端本身的问题
- etcd里面显示的地址的确会出现没有的情况,但是通过日志发现服务端lease维持的心跳却是正常的,一度觉得怀疑人生~~
- 后来发现是下面的原因A导致的,
- 解决后,发现依然有问题,O(∩_∩)O,很奇怪~
- 通过打开grpc日志,发现grpc的client端会收到地址列表为空的问题:balancerWrapper: got update addr from Notify:[]
- 但是etcd里面显示的注册地址却都是存在的,一度觉得怀疑人生~~
- 后来通过阅读grcp源码分析,增加日志排查 :google.golang.org/grpc/balancer.go 文件中 函数 (rr *roundRobin) watchAddrUpdates() 里面,增加 etcd watch的数据变动行为,发现了原因
- 通过下面的原因B的处理方式解决
原因
- 问题存在两个
- 原因A
- 早先服务注册:类似于
key:service/go_server_xx/10.1.1.1:9999
val:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null} -
并且通过etcd的lease机制维持心跳
- 假设一个宿主机起了两个pod,也就两个服务,ser1和ser2
- ser1启动的时候会注册 service/go_server_xx/10.1.1.1:9999 并且绑定 lease1,并且维持lease1心跳
- 之后,ser2启动的时候也会注册 service/go_server_xx/10.1.1.1:9999 并且绑定 lease2,并且维持lease2心跳,住以此时这个key和lease1解绑了
- 当发生弹性伸缩的时候,比如ser2关闭了,此时 lease2过期,同时与其绑定的key也被删除,所以client会发生找不到地址列表的问题
- 但是从服务端ser1来看,依然提供服务,并且与etcd的lease1心跳也正常~~,哈哈
- 早先服务注册:类似于
- 解决方法A
- 通过在key后边增加后缀解决,类似于:service/go_server_xx/lease1/10.1.1.1:9999, 这样即使一个宿主机上有多个服务,各自也会注册各自的
- 原因B
- 上述问题解决之后,还有个问题
-
比如同一个宿主机注册的两个服务地址
key1:service/go_server_xx/lease1/10.1.1.1:9999 val1:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null} 和 key2:service/go_server_xx/lease2/10.1.1.1:9999 val2:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null}
- 在client端watch etcd变化的时候会发现两个add行为,但是 由于val中的地址是一样的,可用addrs列表里面只会保存一个 10.1.1.1:9999,另一个会跳过:
grpclog.Infoln("grpc: The name resolver wanted to add an existing address: ", addr)
- 当发生弹性伸缩的时候,比如ser2关闭了
- watch etcd变化的时候会收到一个del行为,这个时候会删除10.1.1.1:9999
- 这样client可用地址列表里面就是空的了
- grpc的client对象收到 balancerWrapper: got update addr from Notify:[]
- 解决方法B:
-
注册的时候增加metadata做区分就可以了,类似
key1:service/go_server_xx/lease1/10.1.1.1:9999 val1:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":lease1} 和 key2:service/go_server_xx/lease2/10.1.1.1:9999 val2:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":lease2}
-
这样增加的时候,可用地址列表里面就会增加两个元素
-
总结
etcd+grpc的一整套机制(服务注册&服务发现等)是好的
k8s的那一套机制也是好的
但是两者结合使用,就得注意很多地方,要不然会出现很多蛋疼的问题提
更多推荐
已为社区贡献3条内容
所有评论(0)