前言
在云计算和容器化技术发展火热的当下,对于微服务架构,服务注册与发现组件是必不可少的。在传统的服务架构中,服务的规模处于运维人员的可控范围内。当部署服务的多个节点时,一般使用静态配置的方式实现服务信息的设定。在微服务应用中,服务实例的数量和网络地址都是动态变化的,这对系统运维提出了巨大的挑战。因此,动态的服务注册与发现就显得尤为重要。
服务发现的选择
主流服务发现软件的对比:
Software | Consul | zookeeper | etcd |
---|---|---|---|
服务健康检查 | 服务状态,内存,硬盘等 | (弱)长连接,keepalive | 连接心跳 |
多数据中心 | 支持 | - | - |
kv 存储服务 | 支持 | 支持 | 支持 |
一致性 | raft | paxos | raft |
cap | cp | cp | cp |
使用接口(多语言能力) | 支持 http 和 dns | 客户端 | http/grpc |
watch 支持 | 全量/支持long polling | 支持 | 支持long polling |
自身监控 | metrics | - | metrics |
安全 | acl /https | acl | https支持(弱) |
spring cloud 集成 | 已支持 | 已支持 | 已支持 |
优点 | 1.简单易用,不需要集成sdk 2.自带健康检查 3.支持多数据中心 4.提供web管理界面 |
1.功能强大,不仅仅只是服务发现 2.提供watcher机制能实时获取服务提供者的状态 3.dubbo等框架支持 |
1.简单易用,不需要集成sdk 2.可配置性强 |
缺点 | 1.不能实时获取服务信息的变化通知 | 1.没有健康检查 2.需在服务中集成sdk,复杂度高 3.不支持多数据中心 |
1.没有健康检查 2.需配合第三方工具一起完成服务发现 3.不支持多数据中心 |
总的来看,目前Consul 自身功能,和 spring cloud 对其集成的支持都相对较为完善,而且运维的复杂度较为简单,所以我们选择consul作为服务发现工具。
consul介绍
Consul是基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册服务软件,采用Raft算法保证服务的一致性,且支持健康检查。
Consul采用主从模式的设计,使得集群的数量可以大规模扩展,集群间通过RPC的方式调用(HTTP和DNS)。它的结构图如下所示:
- Client:作为一个代理(非微服务实例),它将转发所有的RPC请求到Server中。作为相对无状态的服务,它不持有任何注册信息。
- Server:作为一个具备扩展功能的代理,它将响应RPC查询、参与Raft选举、维护集群状态和转发查询给Leader等。
- Leader-Server:一个数据中心的所有Server都作为Raft节点集合的一部分。其中Leader将负责所有的查询和事务(如服务注册),同时这些事务也会被复制到所有其他的节点。
- Data Center:数据中心作为一个私有的,低延迟和高带宽的一个网络环境。每个数据中心会存在Consul集群,一般建议Server是3-5台(考虑到Raft算法在可用性和性能上取舍),而Leader只能唯一,Client的数量没有限制,可以轻松扩展。
Raft算法
Raft是一种基于Paxos的一致性算法。相比于Paxos,Raft设计采用了较少的状态,并且是一种更简单、更易于理解的算法。
Raft节点总会是Follower、candidate、Leader三个状态之一。Leader处理所有的查询和事务,并向Follower同步事务。Follower会将所有的RPC查询和事务转发给Leader处理,它仅从Leader接受事务的同步。数据的一致性以Leader中的数据为准实现。
在节点初始启动时,节点的Raft状态机将处于Follower状态等待来来自Leader节点的心跳。如果在一定时间周期内没有收到Leader节点的心跳,节点将发起选举。
Follower节点选举时会将自己的状态切换为Candidate,然后向集群中其它Follower节点发送请求,询问其是否选举自己成为Leader。当收到来自集群中过半数节点的接受投票后,节点即成为Leader,开始接收Client的事务处理和查询并向其它的Follower节点同步事务。Leader节点会定时向Follower发送心跳来保持其地位。
一致性模式
为了支持开发人员可能需要的各种权衡,Consul支持3种不同的一致性模式
- Default
Raft用到了leader约期的概念,意思是,在一个时间窗口中,leader认为自己的角色是稳定的。但是,如果leader节点与别的节点被分隔,即发生所谓“脑裂”现象,那么会有一个新的leader节点被选举出来。旧的leader节点将不能提交任何新的log entry, 但是如果它提供了对数据的读取,那么客户端读到的这些值可能是过期的。时间窗口也是有界的,时间到了,leader也会下台。
默认模式是基于leader约期的,因此客户端可能读到过期的值。但是这种模式是对读取速度和一致性的一种取舍,牺牲了某些情况下的强一致性,以换取更高的读取速度。 - consistent
这种模式是强一致性模式。这种模式要求leader节点每次做读和写操作时都要与法定个数的节点去确认自己仍然是leader。 牺牲读的速度,保障了强一致性。 - stale
这种模式允许任何Consul server向外部提供读取操作,无论它是不是leader节点。
这种模式特别快,但是读到过期数据的可能性非常大。这种模式允许在没有leader节点的情况下对读请求做出相应,尽管实际上这时候Raft集群已经是处于不可用状态了
Gossip协议
Gossip协议是为了解决分布式环境下监控和事件通知的瓶颈。Gossip协议中的每个Agent会利用Gossip协议互相检查在线状态,分担了服务器节点的心跳压力,通过Gossip广播的方式发送消息。
所有的Agent都运行着Gossip协议。服务器节点和普通Agent都会加入这个Gossip集群,收发Gossip消息。每隔一段时间,每个节点都会随机选择几个节点发送Gossip消息,其他节点会再次随机选择其他几个节点接力发送消息。这样一段时间过后,整个集群都能收到这条消息。
基于Raft算法,Consul提供强一致性的注册中心服务,但是由于Leader节点承担了所有的处理工作,势必加大了注册和发现的代价,降低了服务的可用性。通过Gossip协议,Consul可以很好地监控Consul集群的运行,同时可以方便通知各类事件,如Leader选择发生、Server地址变更等。
Consul用了两种不同的Gossip池。我们把这两种池分别叫做LAN池和WAN池。
LAN池
Consul中的每个数据中心有一个LAN池,它包含了这个数据中心的所有成员,包括clients和servers。LAN池用于以下几个目的:
成员关系信息允许client自动发现server, 减少了所需要的配置量。
分布式失败检测机制使得由整个集群来做失败检测这件事, 而不是集中到几台机器上。
gossip池使得类似Leader选举这样的事件变得可靠而且迅速。
WAN池
WAN池是全局唯一的,因为所有的server都应该加入到WAN池中,无论它位于哪个数据中心。由WAN池提供的成员关系信息允许server做一些跨数据中心的请求。一体化的失败检测机制允许Consul优雅地去处理:整个数据中心失去连接, 或者仅仅是别的数据中心的某一台失去了连接。
使用docker-compose部署consul集群
建立compose文件consul.yml
docker-compose文件如下:
version: '3.6'
services:
consul1:
image: consul:latest
container_name: consul1
restart: always
network_mode: mynet
command: agent -server -client=0.0.0.0 -bootstrap-expect=