consul服务注册与服务发现的巨坑
最近使用 consul集群多nodeconsul集群的node也就是我们所说的consul实例。集群由多个node组成,为了集群的可用性,需要超过半数的node启用server。如5个node中建议3个启用server模式,3个node组成的集群就2个node启用server模式。 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,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,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没有那么简单! 看着似乎任选一个就可以做到正确删除service了!可以继续说一声,没有那么简单,consul的坑就是多。 选择了 没事不是还有一个接口没有使用吗?再来看看 请查看consul的bugUnable to deregister a service #1188。 解决方案第一步:查询出serviceid所属的servicename所有的列表; 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",err) } err = client.Agent().ServiceDeregister(serviceId) log.Infof("ServiceDeregister host=%v,serviceId=%v",serviceId) } } } } else { err = c.Client.Agent().ServiceDeregister(serviceId) log.Infof("ServiceDeregister serviceId=%v",serviceId) } 可以肯定的是consul还有其他的坑的,但是这两个坑让我记忆深刻,记录下来给准备使用consul或者已经遇到这些坑的同学一个提醒。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |