服务节点是临时节点,当服务在一定时间内没有发送心跳包,则立马剔除
zookeeper从设计者模式角度出发来理解,是一个基于观察者模式设计的分布式服务管理框架
他负责存储和管理大家都关心的数据,然后接收观察者的注册,一旦这些数据发生变化,zookeeper就将负责通知已经在zookeeper上注册的那些观察者做出的相应的反应。
1.第一步,在官网下载相关安装包
https://zookeeper.apache.org/releases.html
2.第二步,找一个自己熟悉的文件夹路径,将下载好的包放入
使用 tar -zxvf apache-zookeeper-3.6.2-bin.tar.gz
3.第三步,解压完之后,cd 进入解压完的包内,在里面创建2个文件夹 data 和logs
4.第四步,cd conf 进入conf配置文件夹,里面有一个zoo_sample.cfg 文件,将其拷贝改名为zoo.conf
cp zoo_sample.cfg zoo.cfg
5.第五步,vi 修改zoo.conf配置,在里面添加红框内容
解析:
tickTime=2000
代表心跳时间,2秒钟一个心跳
initLimit=10
代表心跳针,,10个心跳针,10*2s=20s
也就是说如果Follower如果20s之内没有联系Learder,那么learder会认为找不到了该Follower
syncLimit=5
集群启动后的心跳针,也就是5*2s=10s
跟上面同理,为什么这里是10s,上面是20s,因为项目启动比较耗时
clientPort=2181
监听客户端访问服务端的端口号
server.1=192.168.248.128:2888:3888
server.A=B:C:D
initLimit=10:LF
初始通信时限集群中的Follower跟随者服务器与Leader领导者服务器之间初始连接时能容忍的最多心跳数(tickTime的数量),用它来限定集群中的Zookeeper服务器连接到Leader的时限。
syncLimit =5:LF
同步通信时限
集群中Leader与Follower之间的最大响应时间单位,假如响应超过syncLimit ,Leader认为Follwer死掉,从服务器列表中删除Follwer。
dataDir
数据文件目录+数据持久化路径
主要用于保存Zookeeper中的数据。
clientPort=2181
客户端连接端口,监听客户端连接的端口。
5.第六步,进入到刚创建的data文件夹,使用 touch myid 创建myid的text文件,在里面输入1并保存
6.第七步,修改系统环境变量配置文件
vi ~/.bash_profile
7.第八步,刷新刚刚修改的配置文件 source ~/.bash_profile
8.第九步,关闭防火墙 service iptables stop 关闭开机自启 chkconfig iptables off
9.第十步,进入bin包下,使用zkServer.sh start 命令,不要使用./zkServer.sh
查看状态
<!--zookeeper-discovey-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
server:
port: 8003
spring:
application:
name: cloud-payment-service8003
cloud:
zookeeper:
connect-string: 192.168.2.133:2181 //ip为zookeeper所在ip,端口默认开始2181
可能会出现jar包异常
原因是我们导入的zookeeper依赖自带的依赖版本和在Linux上安装的不一致,导致版本冲突
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--排除zk自带的3.5.3-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加咱门linux上安装的zk 3.4,9版本-->
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
只要有半数以上
节点存活(假设5个挂3个,不能正常服务,4个挂2个,可以正常服务,注意:半数以上),Zookeeper集群就能正常服务。事务
)ZooKeeper数据模型的结构与Unix文件系统很类似
,整体上可以看作是一棵树,每个节点称做一个ZNode。每一个ZNode默认能够存储1MB
的数据,每个ZNode都可以通过其路径唯一标识
。
什么是F5?
F5负载均衡器是应用交付网络的全球领导者F5 Networks公司提供的一个负载均衡器专用设备,F5 BIG-IP LTM的官方名称叫做本地流量管理器,可以做4-7层负载均衡,具有负载均衡、应用交换、会话交换、状态监控、智能网络地址转换、通用持续性、响应错误处理、IPv6网关、高级路由、智能端口镜像、 SSL加速、智能HTTP压缩、TCP优化、第7层速率整形、内容缓冲、内容转换、连接加速、高速缓存、
Cookie加密、选择性内容加密、应用攻击过滤、拒绝服务(DoS)攻击和SYN Flood保护、防火墙一包过滤.包消毒等功能。
选举机制简单版: 假设有5台机器,编号1,2,3,4,5 1号先来,先投自己,发现票数没超过半数,放弃,2号
来了,2号先投自己,1号因为自己废了,也投id大的,此时2号id大,就投2号,此时2号有2票,但是没超过半数,没用,3号来了,3号投自己先,然后1,2号也投3号,此时3号有3票,超过半数,Leader就它了
二、选举流程简述
目前有5台服务器,每台服务器均没有数据,它们的编号分别是1.23.45.按编号依次启动,它们的选择举过程如下:
三、选择机制中的概念
Serverid: 服务器ID
比如有三台服务器,编号分别是123。
编号越大在选择算法中的权重越大。
Zxid: 数据ID
服务器中存放的最大数据ID
值越大说明数据越新,在选举算法中数据越新权重越大
Epoch:逻辑时钟
或者叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。
四、选举消息内容
在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容。
原子广播
,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议
。通过Zab 协议来保证分布式事务的最终一致性
。发现
,同步
,广播
。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。
状态同步保证了leader和Server具有相同的系统状态。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。
所有的提议(proposal)都在被提出的时候加 上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一 个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
epoch:可以理解为皇帝的年号,当新的皇帝leader产生后,将有一个新的epoch年号。
每个Server在工作过程中有四种状态:
LOOKING:当前Server不知道leader是谁,正在搜寻。
LEADING:当前Server即为选举出来的leader。
FOLLOWING:leader已经选举出来,当前Server与之同步。
OBSERVING:观察者状态;表明当前服务器角色是 Observer
Zookeeper 允许客户端向服务端的某个 znode 注册一个 Watcher 监听,当服务端的一些指定事件,触发了这个 Watcher ,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。
大致分为三个步骤:
客户端注册 Watcher
调用 getData、getChildren、exist 三个 API ,传入Watcher 对象。
标记请求request ,封装 Watcher 到 WatchRegistration 。
封装成 Packet 对象,发服务端发送request 。
收到服务端响应后,将 Watcher 注册到 ZKWatcherManager 中进行管理。
请求返回,完成注册。
服务端处理 Watcher
服务端接收 Watcher 并存储。
Watcher 触发
调用 process 方法来触发 Watcher 。
客户端回调 Watcher
客户端 SendThread 线程接收事件通知,交由 EventThread 线程回调Watcher 。
客户端的 Watcher 机制同样是一次性的,一旦被触发后,该 Watcher 就失效了。
小总结:
Zookeeper 有两种选举算法:基于 basic paxos 实现和基于 fast paxos 实现。默认为 fast paxos;
要求集群中的机器为奇数,也是为了选举算法;
Zookeeper 采用了递增的事务 id 来识别,所有的 proposal (提议)都在被提出的时候加上了zxid 。
zxid 实际上是一个 64 位数字。高 32 位是 epoch 用来标识 Leader 是否发生了改变,如果有新的Leader 产生出来, epoch 会自增。 低 32 位用来递增计数。 当新产生的 proposal 的时候,会依据数据库的两阶段过程,首先会向其他的 Server 发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行。
Leader 服务器会和每一个 Follower/Observer 服务器都建立 TCP 连接,同时为每个Follower/Observer 都创建一个叫做 LearnerHandler 的实体。LearnerHandler 主要负责 Leader 和Follower/Observer 之间的网络通讯,包括数据同步,请求转发和 proposal 提议的投票等。Leader 服务器保存了所有 Follower/Observer 的 LearnerHandler 。
(1)ZooKeeper分为服务器端(Server) 和客户端(Client),客户端可以连接到整个ZooKeeper服务的任意服务器上(除非 leaderServes 参数被显式设置, leader 不允许接受客户端连接)。
(2)客户端使用并维护一个 TCP 连接,通过这个连接发送请求、接受响应、获取观察的事件以及发送心跳。如果这个 TCP 连接中断,客户端将自动尝试连接到另外的 ZooKeeper服务器。客户端第一次连接到 ZooKeeper服务时,接受这个连接的 ZooKeeper服务器会为这个客户端建立一个会话。当这个客户端连接到另外的服务器时,这个会话会被新的服务器重新建立。
(3)上图中每一个Server代表一个安装Zookeeper服务的机器,即是整个提供Zookeeper服务的集群(或者是由伪集群组成);
(4)组成ZooKeeper服务的服务器必须彼此了解。它们维护一个内存中的状态图像,以及持久存储中的事务日志和快照, 只要大多数服务器可用,ZooKeeper服务就可用;
(5)ZooKeeper 启动时,将从实例中选举一个 leader,Leader 负责处理数据更新等操作,一个更新操作成功的标志是当且仅当大多数Server在内存中成功修改数据。每个Server 在内存中存储了一份数据。
(6)Zookeeper是可以集群复制的,集群间通过Zab协议(Zookeeper Atomic Broadcast)来保持数据的一致性;
(7)Zab协议包含两个阶段:leader election阶段和Atomic Brodcast阶段。
a) 集群中将选举出一个leader,其他的机器则称为follower,所有的写操作都被传送给leader,并通过brodcast将所有的更新告诉给follower。
b) 当leader崩溃或者leader失去大多数的follower时,需要重新选举出一个新的leader,让所有的服务器都恢复到一个正确的状态。
c) 当leader被选举出来,且大多数服务器完成了 和leader的状态同步后,leadder election 的过程就结束了,就将会进入到Atomic brodcast的过程。
d) Atomic Brodcast同步leader和follower之间的信息,保证leader和follower具有形同的系统状态。
持久化目录节点(PERSISTENT)
客户端与Zookeeper断开连接后,该节点依旧存在
持久化顺序编号目录节点(PERSISTENT_SEQUENTIAL)
客户端与Zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号:
临时目录节点(EPHEMERAL)
客户端与Zookeeper断开连接后,该节点被删除
临时顺序编号目录节点(EPHEMERAL_SEQUENTIAL)
客户端与Zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
注意:
要解决Split-Brain脑裂的问题,一般有下面几种种方法:
Quorums (法定人数) 方式
比如3个节点的集群,Quorums = 2, 也就是说集群可以容忍1个节点失效,这时候还能选举出1个lead,集群还可用。
比如4个节点的集群,它的Quorums = 3,Quorums要超过3,相当于集群的容忍度还是1,如果2个节点失效,那么整个集群还是无效的。
这是zookeeper防止"脑裂"默认采用的方法。
Redundant communications (冗余通信)方式
集群中采用多种通信方式,防止一种通信方式失效导致集群中的节点无法通信。
Fencing (共享资源) 方式
比如能看到共享资源就表示在集群中,能够获得共享资源的锁的就是Leader,看不到共享资源的,就不在集群中。
要想避免zookeeper"脑裂"情况其实也很简单,follower节点切换的时候不在检查到老的leader节点出现问题后马上切换,而是在休眠一段足够的时间,确保老的leader已经获知变更并且做了相关的shutdown清理工作了然后再注册成为master就能避免这类问题了,这个休眠时间一般定义为与zookeeper定义的超时时间就够了,但是这段时间内系统可能是不可用的,但是相对于数据不一致的后果来说还是值得的。
ZooKeeper默认采用了Quorums这种方式来防止"脑裂"现象。即只有集群中超过半数节点投票才能选举出Leader。
这样的方式可以确保leader的唯一性,要么选出唯一的一个leader,要么选举失败。
在zookeeper中Quorums作用如下:集群中最少的节点数用来选举leader保证集群可用。
通知客户端数据已经安全保存前集群中最少数量的节点数已经保存了该数据。
一旦这些节点保存了该数据,客户端将被通知已经安全保存了,可以继续其他任务。而集群中剩余的节点将会最终也保存了该数据。
假设某个leader假死,其余的followers选举出了一个新的leader。
这时,旧的leader复活并且仍然认为自己是leader,这个时候它向其他followers发出写请求也是会被拒绝的。
因为每当新leader产生时,会生成一个epoch标号(标识当前属于那个leader的统治时期),这个epoch是递增的,followers如果确认了新的leader存在,知道其epoch,就会拒绝epoch小于现任leader epoch的所有请求。
那有没有follower不知道新的leader存在呢,有可能,但肯定不是大多数,否则新leader无法产生。
Zookeeper的写也遵循quorum机制,因此,得不到大多数支持的写是无效的,旧leader即使各种认为自己是leader,依然没有什么作用。zookeeper除了可以采用上面默认的Quorums方式来避免出现"脑裂",
快照的缺点,文件太大,而且快照文件不会是最新的数据;
增量事务日志的缺点,运行时间了,日志太多了,加载太慢。
其实就是水平扩容了,Zookeeper 在这方面不太好。
两种方式:
全部重启:关闭所有 Zookeeper 服务,修改配置之后启动。不影响之前客户端的会话。
逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常
用的方式。
注意: 3.5 版本开始支持动态扩容。
都可以作为注册中心的Zookeeper和Eureka,想要理解两种注册中心的区别,首先要理解CAP理论。
CPA理论:
一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在分布式系统中是必须要保证的,因此我们只能在A和C之间权衡。故在此Zookeeper保证的是CP,而Eureka则保证的是AP
Zookeeper
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,单不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。
但是Zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。
问题在于,选举leader的时间太长,30s~120s,并且选举期间整个Zookeeper集群都是不可用的,这就导致在选举期间注册服务瘫痪。
在云部署的环境下,因网络问题是得Zookeeper集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
Eureka
Eureka看明白了这点,因此在设计师就有限保证可用性;
Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时如果发现连接失败,会自动切换至其他节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况: