Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,Consul的方案更“一站式” ,内置了服务注册与发现框 架、具有以下性质:
- 分布一致性协议实现、
- 健康检查、
- Key/Value存储、
- 多数据中心方案,
不再需要依赖其他工具(比如ZooKeeper等)。
使用起来也较 为简单。Consul使用Go语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与Docker等轻量级容器可无缝配合 。 基于 Mozilla Public License 2.0 的协议进行开源. Consul 支持健康检查,并允许 HTTP 和 DNS 协议调用 API 存储键值对. 一致性协议采用 Raft 算法,用来保证服务的高可用. 使用 GOSSIP 协议管理成员和广播消息, 并且支持 ACL 访问控制.
Consul 的使用场景
- 服务注册与发现
- 配置中心
Consul 的优势
使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是 Paxos, 而 etcd 使用的则是 Raft. 支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟, 分片等情况等. zookeeper 和 etcd 均不提供多数据中心功能的支持. 支持健康检查. etcd 不提供此功能. 支持 http 和 dns 协议接口. zookeeper 的集成较为复杂, etcd 只支持 http 协议. 官方提供web管理界面, etcd 无此功能.
Consul内部原理
下面这张图来源于Consul官网,很好的解释了Consul的工作原理,先大致看一下。
首先Consul支持多数据中心,在上图中有两个DataCenter,他们通过Internet互联,同时请注意为了提高通信效率,只有Server节点才加入跨数据中心的通信。
在单个数据中心中,Consul分为Client和Server两种节点(所有的节点也被称为Agent),Server节点保存数据,Client负责健康检查及转发数据请求到Server;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端口完成。
Consul 的角色
client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群.server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其他数据中心通讯. 每个数据中心的 server 数量推荐为 3 个或是 5 个.
由于Spring Cloud Consul项目的实现,我们可以轻松的将基于Spring Boot的微服务应用注册到Consul上,并通过此实现微服务架构中的服务治理。
Consul服务发现原理
下面这张图是自己画的,基本描述了服务发现的完整流程,先大致看一下。
首先需要有一个正常的Consul集群,有Server,有Leader。这里在服务器Server1、Server2、Server3上分别部署了Consul Server,假设他们选举了Server2上的Consul Server节点为Leader。这些服务器上最好只部署Consul程序,以尽量维护Consul Server的稳定。
然后在服务器Server4和Server5上通过Consul Client分别注册Service A、B、C,这里每个Service分别部署在了两个服务器上,这样可以避免Service的单点问题。服务注册到Consul可以通过HTTP API(8500端口)的方式,也可以通过Consul配置文件的方式。Consul Client可以认为是无状态的,它将注册信息通过RPC转发到Consul Server,服务信息保存在Server的各个节点中,并且通过Raft实现了强一致性。
最后在服务器Server6中Program D需要访问Service B,这时候Program D首先访问本机Consul Client提供的HTTP API,本机Client会将请求转发到Consul Server,Consul Server查询到Service B当前的信息返回,最终Program D拿到了Service B的所有部署的IP和端口,然后就可以选择Service B的其中一个部署并向其发起请求了。如果服务发现采用的是DNS方式,则Program D中直接使用Service B的服务发现域名,域名解析请求首先到达本机DNS代理,然后转发到本机Consul Client,本机Client会将请求转发到Consul Server,Consul Server查询到Service B当前的信息返回,最终Program D拿到了Service B的某个部署的IP和端口。
启动Consul集群
在 Windows PowerShell中执行命令拉取最新版本的Consul镜像:
|
然后就可以启动集群了,这里启动4个Consul Agent,3个Server(会选举出一个leader),1个Client。
|
第1个启动容器的IP一般是172.17.0.2,后边启动的几个容器IP会排着来:172.17.0.3、172.17.0.4、172.17.0.5。
这些Consul节点在Docker的容器内是互通的,他们通过桥接的模式通信。但是如果主机要访问容器内的网络,需要做端口映射。在启动第一个容器时,将Consul的8500端口映射到了主机的8900端口,这样就可以方便的通过主机的浏览器查看集群信息。
进入容器consul1:
|
服务注册
自己写一个web服务,用最熟悉的开发语言就好了,不过需要在容器中能够跑起来,可能需要安装运行环境,比如python、java、.net core等的sdk及web服务器,需要注意的是Consul的docker镜像基于alpine系统,具体运行环境的安装请搜索一下。
这里写了一个hello服务,通过配置文件的方式注册到Consul,服务的相关信息:
- name:hello,服务名称,需要能够区分不同的业务服务,可以部署多份并使用相同的name注册。
- id:hello1,服务id,在每个节点上需要唯一,如果有重复会被覆盖。
- address:172.17.0.5,服务所在机器的地址。
- port:5000,服务的端口。
- 健康检查地址:http://localhost:5000/,如果返回HTTP状态码为200就代表服务健康,每10秒Consul请求一次,请求超时时间为1秒。
请将下面的内容保存成文件services.json,并上传到容器的/consul/config目录中。
|
复制到consul config目录:
|
重新加载consul配置:
|
然后这个服务就注册成功了。可以将这个服务部署到多个节点,比如部署到consul1和consul4,并同时运行。
服务发现
服务注册成功以后,调用方获取相应服务地址的过程就是服务发现。Consul提供了多种方式。
HTTP API方式:
|
返回的信息包括注册的Consul节点信息、服务信息及服务的健康检查信息。这里用了一个参数passing=false,会自动过滤掉不健康的服务,包括本身不健康的服务和不健康的Consul节点上的服务,从这个设计上可以看出Consul将服务的状态绑定到了节点的状态。
如果服务有多个部署,会返回服务的多条信息,调用方需要决定使用哪个部署,常见的可以随机或者轮询。为了提高服务吞吐量,以及减轻Consul的压力,还可以缓存获取到的服务节点信息,不过要做好容错的方案,因为缓存服务部署可能会变得不可用。具体是否缓存需要结合自己的访问量及容错规则来确定。
上边的参数passing默认为false,也就是说不健康的节点也会返回,结合获取节点全部服务的方法,这里可以做到获取全部服务的实时健康状态,并对不健康的服务进行报警处理。
DNS方式:
hello服务的域名是:hello.service.dc1.consul,后边的service代表服务,固定;dc1是数据中心的名字,可以配置;最后的consul也可以配置。