分享
  1. 首页
  2. 文章

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

梦朝思夕 · · 2132 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

最近使用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文档

agent/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或者已经遇到这些坑的同学一个提醒。


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:梦朝思夕

查看原文:consul服务注册与服务发现的巨坑

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
2132 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏