consul服务注册与服务发现的巨坑


声明:本文转载自https://my.oschina.net/u/553243/blog/1634206,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

最近使用consul作为项目的服务注册与服务发现的基础功能。在塔建集群使用中遇到一些坑,下面一个个的记录下来。

consul集群多node

consul集群的node也就是我们所说的consul实例。集群由多个node组成,为了集群的可用性,需要超过半数的node启用server。如5个node中建议3个启用server模式,3个node组成的集群就2个node启用server模式。 看到这里的时候你一定觉得没有什么问题呀,但是consul坑就是多。加入你的集群组成如下:

Node          Address              Status  Type    Build  Protocol  DC                    Segment BJ-MQTEST-01  10.163.145.117:8301  alive   server  1.0.6  2         iget-topology-aliyun  <all> BJ-MQTEST-02  10.163.147.47:8301   alive   server  1.0.6  2         iget-topology-aliyun  <all> BJ-TGO-01     10.163.145.110:8301     alive   client  1.0.6  2         iget-topology-aliyun  <default> 

那么client可以使用上述的3个ip连接到consul集群,假设client A使用使用10.163.145.117注册了service,重启后使用地址10.163.145.110注册之前的service信息,此时你就会惊喜的发现,UI可以同时看到在同一个servicename下存在两个相同的serviceid。

这就是consul集群多node的坑,因为service底层虽然使用了KV存储,但是service的KEY与serviceid无关,所以在集群中可以重复。

解决方案一

集群中只有一个node使用server模式,其他的都是client模式。缺点很明显,如果server的node挂了,那么集群的可用性就没有了。

解决方案二

相同的客户端使用相同的node地址,这样就可以确保同一个servicename下不存在两个相同的serviceid。缺点是如果客户端绑定的node挂了,那么client就使用。 代码给出

package registry  import ( 	"fmt" 	"math" 	"net" 	"sort" 	"strings"  	log "github.com/golang/glog" )  type ConsulBind struct { 	Addr  string 	IpInt float64 } type ConsulBindList []ConsulBind  func (s ConsulBindList) Len() int { 	return len(s) } func (s ConsulBindList) Swap(i, j int) { 	s[i], s[j] = s[j], s[i] } func (s ConsulBindList) Less(i, j int) bool { 	return s[i].IpInt < s[j].IpInt } func (s ConsulBindList) ToStrings() []string { 	ret := make([]string, 0, len(s)) 	for _, cbl := range s { 		ret = append(ret, cbl.Addr) 	} 	return ret }  func BingConsulSort(consulAddrs []string) []string { 	localIpStr, err := GetAgentLocalIP() 	if err != nil { 		return consulAddrs 	} 	localIp := net.ParseIP(localIpStr) 	localIpInt := int64(0) 	if localIp != nil { 		localIpInt = util.InetAton(localIp) 	} 	addrslist := make([]ConsulBind, 0, len(consulAddrs)) 	for _, addr := range consulAddrs { 		ads := strings.Split(addr, ":") 		if len(ads) == 2 { 			ip := net.ParseIP(ads[0]) 			if ip != nil { 				ipInt := util.InetAton(ip) 				fmt.Println("ip:", ip, ipInt, localIpInt, (ipInt - localIpInt)) 				addrslist = append(addrslist, ConsulBind{ 					Addr:  addr, 					IpInt: math.Abs(float64(ipInt - localIpInt)), 				}) 			} 		} 	} 	consulBindList := ConsulBindList(addrslist) 	sort.Sort(consulBindList) 	log.Infof("sort addrs %v", consulBindList) 	return consulBindList.ToStrings() }  

解决方案三

客户端随机使用集群中的任意一个地址,但是注册之前先判断该servicename是否已经存在要注册的serviceid了,如果存在就删除重新注册。缺点就是watch会有较多事件,可以升级为如果存在并且是健康的就不允许重复注册,我使用的就是该方案。

删除service

一开始很多人都会觉得服务出现问题了下架了挂了,那么就会被移出了。但是在consul中删除service没有那么简单! 请查看官网文档:
catalog文档
Deregister Entity
agent/service文档
Deregister Service

看着似乎任选一个就可以做到正确删除service了!可以继续说一声,没有那么简单,consul的坑就是多。

选择了/agent/service/deregister/:service_id接口,会发现你无法删除别的node的service。比如10.163.145.117中有个serviceid为agent_xxxx_v1,但是客户端连接consul使用的IP为10.163.145.110,那么就无法删除掉agent_xxxx_v1

没事不是还有一个接口没有使用吗?再来看看/catalog/deregister,执行完成后看了UI,嗯嗯的确是删除了agent_xxxx_v1。等等。。。 。。。 30s后发现agent_xxxx_v1又出现了,这是怎么回事????

请查看consul的bugUnable to deregister a service #1188

解决方案

第一步:查询出serviceid所属的servicename所有的列表;
第二步:遍历列表获取到node的地址后删除所有的serviceid;

if len(c.Options.Addrs) > 0 { 		addrMap := make(map[string]string, len(c.Options.Addrs)) 		for _, host := range c.Options.Addrs { 			addr, _, err := net.SplitHostPort(host) 			if err != nil { 				log.Warningf("%v is err=%v", host, err) 				continue 			} 			addrMap[addr] = host 		} 		rsp, _, _ := c.Client.Health().Service(s.Name, "", false, nil) 		for _, srsp := range rsp { 			if srsp.Service.ID == serviceId {  				if host, ok := addrMap[srsp.Node.Address]; ok { 					config := consul.DefaultNonPooledConfig() 					config.Address = host 					// 创建consul连接 					client, err := consul.NewClient(config) 					if err != nil { 						log.Warningf("NewClient is err=%v", host, err) 					} 					err = client.Agent().ServiceDeregister(serviceId) 					log.Infof("ServiceDeregister host=%v , serviceId=%v", host, serviceId) 				} 			} 		} 	} else { 		err = c.Client.Agent().ServiceDeregister(serviceId) 		log.Infof("ServiceDeregister  serviceId=%v", serviceId) 	} 

可以肯定的是consul还有其他的坑的,但是这两个坑让我记忆深刻,记录下来给准备使用consul或者已经遇到这些坑的同学一个提醒。

本文发表于2018年03月13日 22:38
(c)注:本文转载自https://my.oschina.net/u/553243/blog/1634206,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1820 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1