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

作者:holdtom
链接:https://www.imooc.com/article/271416
来源:慕课网

consul服务注册与服务发现的巨坑的更多相关文章

  1. SpringCloud+Consul 服务注册与服务发现

    SpringCloud+Consul 服务注册与服务发现 1. 服务注册: 在Spring.factories有一段: # Discovery Client Configuration org.spr ...

  2. 【转】用 Consul 来做服务注册与服务发现

    原文:https://segmentfault.com/a/1190000018731395?utm_source=tag-newest ------------------------------- ...

  3. Go微服务框架go-kratos实战04:kratos中服务注册和服务发现的使用

    一.简介 关于服务注册和服务发现介绍,我前面的文章有介绍过 - 服务注册和发现的文章. 作为服务中心的软件有很多,比如 etcd,consul,nacos,zookeeper 等都可以作为服务中心. ...

  4. SpringCloud系列(一):Eureka 服务注册与服务发现

    上一篇,我们介绍了服务注册中心,光有服务注册中心没有用,我们得发服务注册上去,得从它那边获取服务.下面我们注册一个服务到服务注册中心上去. 我们创建一个 hello-service 的 spring ...

  5. SpringCloud之eureka服务注册和服务发现

    服务注册中心 :eureka-server 作用:服务注册中心提供服务注册功能 服务提供方:eureka-client 作用:注册服务到服务注册中心 服务注册中心 :eureka-server 创建 ...

  6. dubbo2.7.X版本带来的服务注册和服务调用方式改变

    参考地址:https://www.cnblogs.com/alisystemsoftware/p/13064620.html 注册中心数据结构格式改变(service:接口服务,application ...

  7. SpringCloud实战之初级入门(二)— 服务注册与服务调用

    目录 1.环境介绍 2.服务提供 2.1 创建工程 2.2 修改配置文件 2.3 修改启动文件 2.5 亲测注意事项 3.服务调用 3.1 创建工程 3.2 修改配置文件 3.3 修改启动文件 3.4 ...

  8. Consul 服务注册与服务发现

    上一篇:Mac OS.Ubuntu 安装及使用 Consul 1. 服务注册 对 Consul 进行服务注册之前,需要先部署一个服务站点,我们可以使用 ASP.NET Core 创建 Web 应用程序 ...

  9. 微服务Consul系列之服务注册与服务发现

    在进行服务注册之前先确认集群是否建立,关于服务注册可以看上篇微服务Consul系列之集群搭建的介绍,两种注册方式:一种是注册HTTP API.另一种是通过配置文件定义,下面讲解的是基于后者配置文件定义 ...

随机推荐

  1. fft相关的复习

    任意长度卷积 CZT 就是一波推导 \[ \begin{aligned} b_i &= \sum_{j=0}^{n-1} \omega^{ij}a_j \\ &= \sum_{j=0} ...

  2. 想学习找不到好的博客?看这里>>

    想学习找不到好的博客?看这里>> (ps:内容 + 作者) 基础数论知识整理--gyh 进阶数论知识整理--又是gyh 关于SPFA--lyj(终于不是gyh) 证明二次探测定理-Line ...

  3. 数据结构实验之排序七:选课名单 (SDUT 3404)

    #include <stdio.h> #include <string.h> #include <stdlib.h> struct node { char data ...

  4. [golang]Go内嵌静态资源go-bindata的安装及使用

    使用 Go 开发应用的时候,有时会遇到需要读取静态资源的情况.比如开发 Web 应用,程序需要加载模板文件生成输出的 HTML.在程序部署的时候,除了发布应用可执行文件外,还需要发布依赖的静态资源文件 ...

  5. P5025 [SNOI2017]炸弹

    原题链接  https://www.luogu.org/problem/P5025 闲话时刻: 第一道 AC 的黑题,虽然众人皆说水... 其实思路不是很难,代码也不是很难打,是一些我们已经学过的东西 ...

  6. 浅谈Python-IO多路复用(select、poll、epoll模式)

    1. 什么是IO多路复用 在传统socket通信中,存在两种基本的模式, 第一种是同步阻塞IO,其线程在遇到IO操作时会被挂起,直到数据从内核空间复制到用户空间才会停止,因为对CPython来说,很多 ...

  7. avalon在公共页面里面写的功能,怎么让某些页面不引用到这个方法和html?

    每一个页面都用用到head.html里面的东西,但是有些页面我不想调用这个js,但是在head.html我已经调用了怎么办? 比如我是这个页面不需要去调用  https://*******/mytry ...

  8. pymongo helper

    import pymongo import click # 数据库基本信息 db_configs = { 'type': 'mongo', 'host': '127.0.0.1', 'port': ' ...

  9. Git是怎么Ignore文件的?

    Git is one of the most popular version control systems (VCS) available, especially thanks to hosting ...

  10. Centos7 中查找文件、目录、内容

    1.查找文件 find / -name ‘filename’ 2.查找目录 find / -name ‘path’ -type d 3.查找内容 find . | xargs grep -ri ‘co ...