Consul简介
- 注册中心(不仅仅是注册中心)
- 一致性协议采用 Raft 算法,用来保证服务的高可用和一致性,实际是CP的模型,并且提供了可配置的3种一致性模式(stale、consistent、default)
- 具有键值对存储功能
- 允许 HTTP 和 DNS 协议调用 API,执行一些业务操作,比如存储键值对(./consul kv get user/config/connections , http://localhost:8500/v1/kv/?recurse 查看所有键值对)
- 自带web界面来查看和执行一些操作
- 支持多数据中心
- Consul 丰富的健康检查(http接口、脚本、tcp等形式定时任务检测)
- 支持 ACL 访问控制.
- 由 HashiCorp 公司用 Go 语言开发
- 使用 GOSSIP 协议管理成员和广播消息
Consul 、Eureka、Zookeeper 比拼
- Eureka最单纯,定位就是注册中心,在CAP模型里面,追求的是AP,牺牲一致性,追求服务的高可用。Eureka集群模式中,大家地位平等,自己监听到了新的注册应用,该Eureka节点会通过RPC相互转告。默认开启保护功能,即使是超时的注册服务,也不会从Eureka中注销。
- Consul扩展了很多功能,除了注册中心、也支持做配置中心、键值对存储、丰富形式的健康检查、多数据中心、Acl控制、日志快照生成等功能
- Zookeeper自己的定位是分布式协调器,也扩展了很多的功能:分布式锁、Watcher通知机制、分布式下的队列管理、键值对存储
- Consul追求CP,一致性通过Raft算法实现的,典型的Paxos的变种算法,Leader、follower、候选者角色,选举时主要看日志是否最新来控制的,但是有3种一致性模式来满足不同的业务场景。
- Zookeeper追求CP,一致性通过Zab协议实现的,也是典型的Paxos的变种算法,角色有Leader、Follower、Observer,选举的时候都是半数通过,选举时主要是基于zxid来控制的。
- Eureka和Consul都自带UI界面来支持管理功能,ZK不支持
- 在事务请求时或者网络抖动时,Eureka都是最快的,因为ZK和Consul都为了确保一致性,会涉及投票半数通过这个等待过程。在网络抖动的时候,ZK和Consul会重新选举,功能暂时不可用,而Eureka不存在
- 健康检查失效节点剔除功能,Consul默认是通过http接口,也就是Provider的actuator/health来实现的,超时会剔除故障Provider,而Eureka通过自身的心跳功能实现Provider的健康检查,不依赖actuator
CAP理论
- C数据一致性,不管在任何时候,从集群中任意一个节点读取到的数据应该一样,
- A高可用性,不会因为某个结点的故障而导致整个集群等待不可用
- P分区容错性,集群的节点之间的通讯,可能因为某些原因,比如网络,比如某个结点宕机,也要确保整个集群的功能可用
如果此时要保持整个集群可用,因为P是无法避免的,所以一般CAP理论,要么选择CP,要么选择AP,无法做到兼顾。
实际上Zookeeper和Consul实现的强一致性也不是100%的一致性,只需要半数机器Ack Purpose后,Leader就认为这个事务成立,commit本地日志后,就响应客户端了,同时会通知其他Follower Commit。
基本原理
- Consul集群下,Consul Leader和Consul Follower之间通过定时发送心跳,检测各个节点间的网络通畅,从而确保数据一致。Follower节点在超时还未接收到心跳,就会发起选举。
- Consul和provider、Consumer之间都保持默认10秒间隔的心跳,从而保证注册在Consul的Provider仍然可用,以及Consumer手中拿到的provider-list是最新的
- Consul中所有的节点分为:Client模式和Server模式(也包括Server模式节点刚启动时的BootStrap模式)(所有的节点也被称为Agent),Server模式下的节点,参与一致性投票、存储和追加日志、可能会直接处理查询的请求(stale模式下),而Client负责健康检查及转发数据请求到Server;
Consul注册中心的原理
- 当Provider 启动的时候,会向 Consul 发送一个 post 请求,告诉 Consul 自己的 IP 和 Port
- Consul 接收到 Provider 的注册后,每隔10s(默认)会向Provider发送一个健康检查的请求,检验Provider是否健康
- 当 Consumer 发送 GET 方式请求 /api/address 到 Provider 时,会先从 Consul 中拿到一个存储服务 IP 和 Port 的临时表,从表中拿到 Provider 的 IP 和 Port 后再发送 GET 方式请求 /api/address
- 该临时表每隔10s会更新,只包含有通过了健康检查的 Provider
Consul的一致性
https://blog.csdn.net/u012422829/article/details/77803799
读取的请求
Consul支持3种模式的一致性选择:stale模式、consistent模式、default模式
- stale模式和Eureka有点类似,在执行读取操作的时候,所有的节点的地位是一样,都可以直接返回给客户端,当前节点的数据,虽然可能此时该节点的数据不是最新的,但是读取的速度最快
- consistent模式是强一致性模式,所有的读取请求都需要转发给Leader节点,Leader节点每次做读和写操作时都要与法定个数的节点去确认自己仍然是leader。 牺牲读的速度,保障了强一致性
- default模式,因为leader节点是定时的发起心跳,来判断自己和各个节点的连接状态,那么在这个心跳的空闲期,它会以为自己还是Leader,所有的读取请求都需要转发给Leader节点,Leader节点无需和其它节点确认自己是否是Leader节点,直接返回结果,但是这种模式是对读取速度和一致性的一种取舍,牺牲了某些情况下的强一致性,以换取更高的读取速度。比如:leader节点与别的节点被分隔,即发生所谓“脑裂”现象,那么会有一个新的leader节点被选举出来。旧的leader节点将不能提交任何新的log entry, 但是如果它提供了对数据的读取,那么客户端读到的这些值可能是过期的。
事务的请求
日志复制(类似于ZAB协议):Raft中,所有的事务操作都需要交给Leader来处理,leader通过强制followers复制自己的logs来处理不一致。这意味着,在follower中logs冲突的entries将会被leader logs中的覆写。
- 任意节点收到客户端发起的事务请求的时候,会把该事务转发给Leader服务器
- Leader先把该日志追加到本地的Log中,然后把该Entry同步给其他Follower
- Follower接收到日志后记录日志然后向Leader发送ACK
- 当Leader收到大多数(n/2)+1 Follower的ACK信息后将该日志设置为已提交并追加到本地磁盘中,通知客户端
- 并在下个heartbeat中Leader将通知所有的Follower将该日志存储在自己的本地磁盘中
Consul的选举Leader
https://www.consul.io/docs/guides/leader-election.html
Consul里面有一个标识leader角色的 K/V键值对,key的话类似于 service/<service name>/leader,value记录了该leader对应的sessionid
- 结合Raft协议,当一个follower和leader的心跳超时了,晋升为Candidate,会发起一次选举
- 该节点(可能同时有多个节点都会发起这个操作)会创建一个session,创建成功后会获得一个sessionid
- 然后发起尝试修改leader键的值为自己,就是尝试成为leader
- 这个时候触发Raft选举,如果通过半数通过的话,成为新的leader,leader键值对的value就是该server的sessionid
- 非Leader的节点,会通过读取这个键值对来确定谁是leader,并且把事务请求(可能也包括读取请求)转发给当前任期的leader
Raft选举Leader
https://www.consul.io/docs/internals/consensus.html
Raft状态下所有的节点有3种状态:Leader、Follower、Candidate。
有一个任期的概念,类似于朝代,也类似于计数器,每个server各自都有一个,老Leader故障时,被一个follower察觉后,该follower会自增自己的当前任期,发起投票。
触发条件:
- 一般情况下,追随者接到领导者的心跳时,把自己的ElectionTimeout清零,不会触发;
- 如果领导者故障,追随者的ElectionTimeout超时发生时,会变成候选者,触发领导人选取;
候选操作过程:
追随者自增当前任期,转换为Candidate,对自己投票,并发起RequestVote RPC,等待下面三种情形发生;
- 自己成功:获得超过半数服务器的投票,赢得选举,成为领导者;
- 别人成功:另一台服务器赢得选举,并接收到对应的心跳,成为追随者;
- 都失败:选举超时,没有任何一台服务器赢得选举,自增当前任期,重新发起选举;
选举限制:投票阻止没有全部日志条目的服务器赢得选举
- 比较数据,如果投票者的日志比候选人的新,拒绝投票请求;这意味着要赢得选举,候选者的日志至少和大多数服务器的日志一样新,那么它一定包含全部的已经提交的日志条目。
- 比较任期,投票人会拒绝任何任期小于当前的RPC请求,包括添加日志请求和发起投票的RPR请求,任期小的节点数据肯定是过时的
- 先到先得,一个server收到其它候选者的vote投票,因为在一个任期里,多个候选者都发觉Leader挂了,几乎同时向该server发送vote投票,所以server在一个term肯定只能投一个,而且投给最先发起的那个
注意事项:
- 候选者等待投票时,可能会接收到来自其它声明为领导人的的AppendEntries RPC。如果该领导人的任期(RPC中有)比当前候选人的当前任期要大,则候选人认为该领导人合法,并转换成追随者;如果RPC中的任期小于候选人的当前任期,则候选人拒绝此次RPC,继续保持候选人状态;
- 候选人既没有赢得选举也没有输掉选举:如果许多追随者在同一时刻都成为了候选人,选票会被分散,可能没有候选人能获得大多数的选票。当这种情形发生时,每一个候选人都会超时,并且通过自增任期号和发起另一轮 RequestVote RPC 来开始新的选举。然而,如果没有其它手段来分配选票的话,这种情形可能会无限的重复下去。所以Raft使用的随机的选举超时时间(150~300ms之间),来避免这种情况发生。
解决的现实情况:
- 老的Leader真挂了,会触发选举,最终会让数据最新的节点成为新的leader
- 如果一个follower自己和leader的网络有延迟,导致没有接收到心跳,误发起投票,但是数据不是最新,选举肯定失败
Raft其它
Log:所有的写操作通过一系列有序的Entry日志组成,数据的一致性通过,拷贝日志文件来实现
投票有效人数:也就是执行一个事务操作,需要过半的Server ACK后才行
Leader,在任意时间节点,都只有一个Node是Leader地位,Leader负责添加新的Log,以及发起Propose,收集ACK和发起Commit
Consul在工作流程中RPC有三种:
- RequestVote RPC:候选人在选举期间发起
- AppendEntries RPC:领导人发起的一种心跳机制,复制日志也在该命令中完成
- InstallSnapshot RPC: 领导者使用该RPC来发送快照给太落后的追随者。
为了提升选举的速度和写的速度,Consul节点分为Client模式和Server模式。只有以server模式运行的Consul节点,才会被认为是Raft节点集的一部分。所有的client节点会把收到的请求转发到server节点中。这么设计的原因主要是出于性能方面的考虑:节点集中的个数越多,那么法定个数的值也就越大,这会导致leader节点可能需要等待数百个follower节点对一条log entry的ack信息。
如果follower出现了问题,以及问题后的重连
- leader后续发送给掉线节点的 RequestVote和AppendEntries的所有RCP都会失败,Raft算法中处理这类失败就是简单的Leader无限发起重试
- 问题服务器重新可用,也会重试失败之前的那个操作,如果问题服务器完成了一个RPC,但是在响应Leader前崩溃了(比如ACK某条日志),那么当他再次可用的时候还会收到相同的RPC请求,此时接收服务器(leader)负责检查,比如如果收到了已经包含该条日志的RPC请求,可以直接忽略这个请求,确保对系统是无害的。
- raft支持日志压缩成快照,来快速同步数据,降低存储的压力,与Raft其它操作Leader-Based不同,snapshot是由各个节点独立生成的。除了日志压缩这一个作用之外,snapshot还可以用于同步状态:slow-follower以及new-server,Raft使用InstallSnapshot RPC完成该过程
consul 的键值对功能
# 往consul里面 key为 user/config/connections的 value值为 5
./consul kv put user/config/connections 5
# get 值
./consul kv get user/config/connections
Consul 之健康检查功能
Consul支持多种方式的健康检查功能:脚本、http接口、tcp等等
集成SpringCloud的项目中,一般都是通过http接口,定时调用Provider的actuator的/health接口来实现的,支持自定义配置healthCheckInterval时间,而且过期的应用会从Consul的服务列表中删除掉。
- 定时任务的脚本(我试了shell失败了),根据脚本执行的返回码 0 passing 1 warning 其它都是failing,执行成功返回,执行失败
- http接口的返回码,200,
HTTP GET http://G5CG72855HGE.logon.ds.ge.com:8501/actuator/health: 200 Output: {"status":"UP"}
安装和启动
下载之后,进入到 C:\huangzs\software 目录下: ./consul agent -dev
然后访问 http://localhost:8500
Consul的注册中心的使用
- 先启动consul服务
- Consumer和Provider添加两个依赖jar包
- spring-boot-starter-actuator 健康检查依赖于此包。
- spring-cloud-starter-consul-discovery Spring Cloud Consul 的支持。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
- 配置文件
spring.application.name=spring-cloud-consul-producer
server.port=8501
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.serviceName=service-producer
- Provider的启动类添加@EnableDiscoveryClient, Consumer的启动类不需要配置这个,因为没必要暴露自己的服务,Eureka也一样
参考资料
http://www.ityouknow.com/springcloud/2018/07/20/spring-cloud-consul.html
Consul官方文档
https://www.consul.io/docs/guides/leader-election.html
Consul中文文档
https://blog.csdn.net/y435242634/article/details/78957447