前言
在SpringCoud核心组件中一文,首先提到了SringCoud注册中心Eureka,后台Eureka官方已不再维护,于是有了Eureka的替代方案Consul,下面具体说一下Consul注册中心。
一、Consul服务注册中心的整体架构
1、consul内部原理
首先Consul支持多数据中心,在上图中有两个DataCenter,他们通过Internet互联,同时请注意为了提高通信效率,只有Server节点才加入跨数据中心的通信。
在单个数据中心中,Consul分为Client和Server两种节点(所有的节点都具备Agent的功能)。
- Consule Server节点:保存整个集群的数据,
- Consule Client负责健康检查及转发数据请求到Server(Client节点当然也保存注册到本节点的服务数据);
- Server节点有一个Leader和多个Follower,Leader节点会将数据同步到Follower,Server的数量推荐是3个或者5个,在Leader挂掉的时候会启动选举机制产生一个新的Leader。
集群内的Consul节点通过gossip协议(流言协议)维护成员关系,也就是说某个节点了解集群内现在还有哪些节点,这些节点是Client还是Server。单个数据中心的流言协议同时使用TCP和UDP通信,并且都使用8301端口。跨数据中心的流言协议也同时使用TCP和UDP通信,端口使用8302。
集群内数据的读写请求既可以直接发到Server,也可以通过Client使用RPC转发到Server,请求最终会到达Leader节点,在允许数据轻微陈旧的情况下,读请求也可以在普通的Server节点完成,集群内数据的读写和复制都是通过TCP的8300端口完成。
2、consul服务发现原理
服务发现的完整流程:
上面是一个consul集群。
(1)、服务器Server1、Server2、Server3上分别部署了Consul Server,假设通过选举,得到Server2上的Consul Server节点为Leader,其它为Follower。
(2)、服务器Server4和Server5上,通过Consul Client分别注册微服务A、B、C
,这里每个微服务分别部署在了两个服务器上
微服务注册到Consul方式:
- 可以通过HTTP API(8500端口)的方式,
- 也可以通过Consul配置文件的方式。
Consul Client会将注册信息通过RPC转发到Consul Server,服务信息保存在Server的各个节点中,并且通过Raft实现了强一致性;
Consul Client还会对注册到当前节点的服务进行健康检查,并将服务状态同步到Server,当然也是强一致性的。
(3)、服务器Server6中微服务 D
需要访问微服务B
,这时候微服务D
首先访问本机Consul Client提供的HTTP API,本机Client会将请求转发到Consul Server,Consul Server查询到微服务 B
当前的信息返回,最终微服务D
拿到了微服务B
的所有部署的IP和端口,然后就可以选择微服务B
的其中一个部署并向其发起请求了。
3、docer安装consul
安装Docker
通过这个地址下载安装:
https://store.docker.com/editions/community/docker-ce-desktop-windows
安装完成后打开 Windows PowerShell,输入docker –version,如果正常输出docker版本就可以了。
启动Consul集群
在 Windows PowerShell中执行命令拉取最新版本的Consul镜像:
docker pull consul
然后就可以启动集群了,这里启动4个Consul Agent(3个Server,1个Client)。3个Server中会选举出一个leader。
#启动第1个Server节点,集群要求要有3个Server,将容器8500端口映射到主机8900端口,同时开启管理界面
docker run -d --name=consul1 -p 8900:8500 -e CONSUL_BIND_INTERFACE=eth0 consul agent --server=true --bootstrap-expect=3 --client=0.0.0.0 -ui
#启动第2个Server节点,并加入集群
docker run -d --name=consul2 -e CONSUL_BIND_INTERFACE=eth0 consul agent --server=true --client=0.0.0.0 --join 172.17.0.2
#启动第3个Server节点,并加入集群
docker run -d --name=consul3 -e CONSUL_BIND_INTERFACE=eth0 consul agent --server=true --client=0.0.0.0 --join 172.17.0.2
#启动第4个Client节点,并加入集群
docker run -d --name=consul4 -e CONSUL_BIND_INTERFACE=eth0 consul agent --server=false --client=0.0.0.0 --join 172.17.0.2
第1个启动容器的IP一般是172.17.0.2,后边启动的几个容器IP会排着来:172.17.0.3、172.17.0.4、172.17.0.5。
进入容器consul1:
docker exec -it consul1 /bin/sh
#执行ls后可以看到consul就在根目录
ls
输入exit可以跳出容器。
4、Consul的健康检查
Consul做服务发现是专业的,健康检查是其中一项必不可少的功能,其提供Script/TCP/HTTP+Interval,以及TTL等多种方式。服务的健康检查由服务注册到的Agent来处理,这个Agent既可以是Client也可以是Server。
集中式的心跳机制,比如传统的Eureka,是让各个服务都必须每隔一定时间发送心跳到Eureka Server。
如果一段时间没收到心跳,那么就认为这个服务宕机了。
但是这种集中式的心跳机制会对Eureka Server造成较大的心跳请求压力,实际上平时Eureka Server接收最多的请求之一就是成千上万服务发送过来的心跳请求。
所以Consul在这块进行了架构优化,引入了Agent概念
。
每个机器上的Consul Agent会不断的发送请求检查服务是否健康,是否宕机。如果服务宕机了,那么就会通知Consul Server。
Consul与ZooKeeper、etcd的区别
后边这两个工具是通过键值存储来实现服务的注册与发现。
- ZooKeeper利用临时节点的机制,业务服务启动时创建临时节点,节点在服务就在,节点不存在服务就不存在。
- etcd利用TTL机制,业务服务启动时创建键值对,定时更新ttl,ttl过期则服务不可用。
健康检查能不能支持故障转移?
上边提到健康检查是由服务注册到的Agent来处理的,那么如果这个Agent挂掉了,会不会有别的Agent来接管健康检查呢?答案是否定的
5、Consul的其它部署架构
如果你实在不想在每个主机部署Consul Client,还有一个多路注册的方案可供选择,这是交流群中获得的思路
如图所示,在专门的服务器上部署Consul Client,然后每个服务都注册到多个Client,这里为了避免服务单点问题还是每个服务部署多份,需要服务发现时,程序向一个提供负载均衡的程序发起请求,该程序将请求转发到某个Consul Client。这种方案需要注意将Consul的8500端口绑定到私网IP上,默认只有127.0.0.1。
这个架构的优势:
- Consul节点服务器与应用服务器隔离,互相干扰少;
- 不用每台主机都部署Consul,方便Consul的集中管理;
- 某个Consul Client挂掉的情况下,注册到其上的服务仍有机会被访问到;
但也需要注意其缺点:
- 引入更多技术栈:负载均衡的实现,不仅要考虑Consul Client的负载均衡,还要考虑负载均衡本身的单点问题。
- Client的节点数量:单个Client如果注册的服务太多,负载较重,需要有个算法(比如hash一致)合理分配每个Client上的服务数量,以及确定Client的总体数量。
- 服务发现要过滤掉重复的注册,因为注册到了多个节点会认为是多个部署(DNS接口不会有这个问题)。
这个方案其实还可以优化,服务发现使用的负载均衡可以直接代理Server节点,因为相关请求还是会转发到Server节点,不如直接就发到Server。
是否可以只有Server?
这个问题的答案还是有关服务数量的问题,首先Server的节点数量不是越多越好,3个或者5个是推荐的数量,数量越多数据同步的处理越慢(强一致性);然后每个节点可以注册的服务数量是有上限的,这个受限于软硬件的处理能力。所以如果你的服务只有10个左右,只有Server问题是不大的,但是这时候有没有必要使用Consul呢?因此正常使用Consul的时候还是要有Client才好,这也符合Consul的反熵设计。
大家可以将这个部署架构与前文提到的普适架构对比下,看看哪个更适合自己,或者你有更好的方案欢迎分享出来。
6、Consul如何通过Raft协议实现强一致性?
首先,Consul Server是部署集群的,而且他会选举出来一台Server作为Leader。
接下来各个服务发送的注册请求都会落地给Leader,由Leader同步给其他Follower。
所以首先第一点,Leader Server是绝对有最新的服务注册信息的。
比如库存服务发起注册了,那么Leader Server上一定有库存服务的注册信息。
接着如果比如订单服务要发现库存服务的话,这个查询请求会发送给Leader Server。
这样服务注册和发现,都是通过一台Leader Server来进行的,就可以保证服务注册数据的强一致性了,大家看下图。
假如库存服务在注册的时候数据刚写到Leader Server,结果Leader Server就宕机了?
那么此时这条注册数据就丢失了,订单服务就没法发现那个库存服务了。没关系,这里Consul会基于Raft协议来解决这个问题。
首先,库存服务注册到Leader Server的时候,会采取Raft协议,要求必须让Leader Server把这条注册数据复制给大部分的Follower Server才算成功。
这就保证了,如果你认为自己注册成功了,那么必然是多台Consul Server都有这条注册数据了。
如果你刚发送给Leader Server他自己就宕机了,那么这次注册会认为失败。
此时,Consul Server集群会重新选举一个Leader Server出来,你需要再次重新注册。
这样就可以保证你注册成功的数据绝对不会丢,然后别人发现服务的时候一定可以从Leader Server上获取到最新的强一致的注册数据。
原文:
http://blog.bossma.cn/consul/consul-service-register-and-discovery-style/