consul服务注册与服务发现的巨坑
最近使用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服务注册与服务发现的巨坑的更多相关文章
- SpringCloud+Consul 服务注册与服务发现
SpringCloud+Consul 服务注册与服务发现 1. 服务注册: 在Spring.factories有一段: # Discovery Client Configuration org.spr ...
- 【转】用 Consul 来做服务注册与服务发现
原文:https://segmentfault.com/a/1190000018731395?utm_source=tag-newest ------------------------------- ...
- Go微服务框架go-kratos实战04:kratos中服务注册和服务发现的使用
一.简介 关于服务注册和服务发现介绍,我前面的文章有介绍过 - 服务注册和发现的文章. 作为服务中心的软件有很多,比如 etcd,consul,nacos,zookeeper 等都可以作为服务中心. ...
- SpringCloud系列(一):Eureka 服务注册与服务发现
上一篇,我们介绍了服务注册中心,光有服务注册中心没有用,我们得发服务注册上去,得从它那边获取服务.下面我们注册一个服务到服务注册中心上去. 我们创建一个 hello-service 的 spring ...
- SpringCloud之eureka服务注册和服务发现
服务注册中心 :eureka-server 作用:服务注册中心提供服务注册功能 服务提供方:eureka-client 作用:注册服务到服务注册中心 服务注册中心 :eureka-server 创建 ...
- dubbo2.7.X版本带来的服务注册和服务调用方式改变
参考地址:https://www.cnblogs.com/alisystemsoftware/p/13064620.html 注册中心数据结构格式改变(service:接口服务,application ...
- SpringCloud实战之初级入门(二)— 服务注册与服务调用
目录 1.环境介绍 2.服务提供 2.1 创建工程 2.2 修改配置文件 2.3 修改启动文件 2.5 亲测注意事项 3.服务调用 3.1 创建工程 3.2 修改配置文件 3.3 修改启动文件 3.4 ...
- Consul 服务注册与服务发现
上一篇:Mac OS.Ubuntu 安装及使用 Consul 1. 服务注册 对 Consul 进行服务注册之前,需要先部署一个服务站点,我们可以使用 ASP.NET Core 创建 Web 应用程序 ...
- 微服务Consul系列之服务注册与服务发现
在进行服务注册之前先确认集群是否建立,关于服务注册可以看上篇微服务Consul系列之集群搭建的介绍,两种注册方式:一种是注册HTTP API.另一种是通过配置文件定义,下面讲解的是基于后者配置文件定义 ...
随机推荐
- Spring入门(四)——整合Mybatis
1. 准备jar包及目录结构 2. 配置db.properties driver = com.mysql.jdbc.Driver url = jdbc:mysql://127.0.0.1:3306/H ...
- Linux下搭建iSCSI共享存储的方法 TGT 方式 CentOS6.9系统下
iSCSI(internet SCSI)技术由IBM公司研究开发,是一个供硬件设备使用的.可以在IP协议的上层运行的SCSI指令集,这种指令集合可以实现在IP网络上运行SCSI协议,使其能够在诸如高速 ...
- php读取邮件
<?php header("Content-type: text/html; charset=utf-8"); class mail { private $server='' ...
- mac 安装 pycharm
下载安装 链接:https://pan.baidu.com/s/19Hm6yZPL_mOTVAb5YQBZKA 密码:j73n 激活码 56ZS5PQ1RF-eyJsaWNlbnNlSWQiOiI1N ...
- Class T泛型和通配符泛型的区别
平时看java源代码的时候,如果碰到泛型的话,我想? T K V E这些是经常出现的,但是有时想不起来代表什么意思,今天整理下: ? 表示不确定的java类型. T 表示java类型. K V 分别代 ...
- CF1214题解
D 因为可以用贡献2把起点终点堵掉,所以答案为0/1/2 0/2简单 1:方格可以理解为分层图,考虑每个能到达终点,起点能到达其的点,标记一下,对角线如果仅存在1则为必经之路 E \(d_i\le n ...
- SSH如何使用
如何查看SSH服务是否已经安装? 在终端命令行执行 rpm -qa | grep "ssh" 执行结果请看下图,说明已经安装:其实在安装linux操作系统的时候默认就会安装上的. ...
- OpenFOAM-双柱及群柱绕流
这次的教程是紧接前几次的教程,设置与前几次教程类似,但是对于设置上稍微有一点点区别,就是在设置值的时候,出现了$internalField,其实这是一个字符串替换,就是在出现$internalFiel ...
- Coupled和segregated【转载】
转载自:http://blog.sina.com.cn/s/blog_67873f6c0100ltq6.html 问题1: 我看中文帮组里说是'分离'的意思?我绝对翻译不太好,请问有更好的翻译吗? 和 ...
- kubernetes(K8S)创建自签TLS证书
TLS证书用于进行通信使用,组件需要证书关系如下: 组件 需要使用的证书 etcd ca.pem server.pem server-key.pem flannel ca.pem server.pem ...