一、ZooKeeper产生背景
众所周知通常分布式架构都是中心化的设计,就是一个主控机连接多个处理节点。 问题可以从这里考虑,当主控机失效时,整个系统则就无法访问了,所以保证系统的高可用性是非常关键之处,也就是要保证主控机的高可用性。分布式锁就是一个 解决该问题的较好方案,多主控机抢一把锁。在这里我们就涉及到了我们的重点Zookeeper。
1,ZooKeeper是什么?
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户
2,Zookeeper设计目的
- 最终一致性:client不论连接到哪个Server,展示给它都是同一个视图。
-
可靠性:具有简单、健壮、良好的性能,如果消息被到一台服务器接受,那么它将被所有的服务器接受。
-
实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
-
等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。
-
原子性:更新只能成功或者失败,没有中间状态。
-
顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面
3,ZooKeeper 应用场景
文件系统
每个子目录项如 NameService 都被称作为znode,和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。
有四种类型的znode:
- PERSISTENT-持久化目录节点
- 客户端与zookeeper断开连接后,该节点依旧存在
- PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
- 客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
- EPHEMERAL-临时目录节点
- 客户端与zookeeper断开连接后,该节点被删除
- EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
- 客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
通知机制
客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。
Zookeeper命名服务
在zookeeper的文件系统里创建一个目录,即有唯一的path。在我们使用tborg无法确定上游程序的部署机器时即可与下游程序约定好path,通过path即能互相探索发现。
Zookeeper的配置管理
程序需要配置,机器一多就比较麻烦了,要逐个改? ,自从用了zookeeper,就简单多了,它会把配置统一管理,并对相对应的程序进行监听,一旦配置变了,每个应用程序就会收到zookeeper的通知,然后从zookeeper上获取最新的配置即可。
![]()
二、Zookeeper集群管理
所谓集群管理无在乎两点:是否有机器退出和加入、
选举master
。
对于第一点,所有机器约定在父目录GroupMembers下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于是,所有人都知道:它上船了。
新机器加入也是类似,所有机器收到通知:新兄弟目录加入,highcount又有了,对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。
Zookeeper分布式锁
有了zookeeper的
一致性
文件系统,锁的问题变得容易。锁服务
可以分为两类:
- 保持独占
对于第一类,我们将zookeeper上的一个znode
看作是一把锁
,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock
节点就释放出锁。
- 控制时序
对于第二类, /distribute_lock 已经预先存在
,所有客户端在它下面创建临时顺序编号
目录节点,和选master一样,编号最小的获得锁
,用完删除,依次方便。
Zookeeper队列管理
两种类型的队列:
- 同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。
-
队列按照 FIFO 方式进行入队和出队操作。(First Input First Output的缩写,
先入先出队列
)
第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。
第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。
分布式与数据复制
Zookeeper作为一个集群提供一致的数据服务,自然,它要在所有机器间做数据复制。数据复制的好处:
容错
:一个节点出错,不致于让整个系统停止工作,别的节点可以接管它的工作;-
提高系统的扩展能力
:把负载分布到多个节点上,或者增加节点来提高系统的负载能力; -
提高性能
:让客户端本地访问就近的节点,提高用户访问速度。
客户端读写访问方式
从客户端读写访问的透明度来看,数据复制集群系统分下面两种
- 写主(WriteMaster) :对数据的修改提交给指定的节点。读无此限制,可以读取任何一个节点。这种情况下客户端需要对读与写进行区别,俗称读写分离;
-
写任意(Write Any):对数据的修改可提交给任意的节点,跟读一样。这种情况下,客户端对集群节点的角色与变化透明。
对zookeeper来说,它采用的方式是写任意。通过增加机器,它的读吞吐能力和响应能力扩展性非常好,而写,随着机器的增多吞吐能力肯定下降(这也是它建立observer的原因),而响应能力则取决于具体实现方式,是延迟复制保持最终一致性,还是立即复制快速响应。
Zookeeper工作原理
Zookeeper的核心是
原子广播
,这个机制保证了各个Server之间的同步
。实现这个机制的协议叫做Zab协议
。Zab协议有两种模式,它们分别是恢复模式
(选主)和广播模式
(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
Zookeeper 下 Server工作状态
- LOOKING:当前Server不知道leader是谁,正在搜寻
-
LEADING:当前Server即为选举出来的leader
-
FOLLOWING:leader已经选举出来,当前Server与之同步
Zookeeper选主流程
当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。
二、Zookeeper安装部署
1,环境说明
[root@aiche-node02 server]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
[root@aiche-node02 server]# uname -a
Linux aiche-node02 3.10.0-957.27.2.el7.x86_64 #1 SMP Mon Jul 29 17:46:05 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
[root@aiche-node02 server]# getenforce
Disabled
2,JDK 部署
tar xf jdk-8u60-linux-x64.tar.gz -C /usr/local/
ln -s /usr/local/jdk1.8.0_60 /usr/local/jdk
sed -i.ori '$a export JAVA_HOME=/usr/local/jdk\nexport PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH\nexport CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar' /etc/profile
source /etc/profile
java -version
#java version "1.8.0_60"
#Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
#Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
3,Zookeeper单机部署
下载地址:https://archive.apache.org/dist/zookeeper/
mkdir /home/tools
cd /home/tools
tar xf zookeeper-3.4.9.tar.gz -C /usr/local/
ln -s /usr/local/zookeeper-3.4.9 /usr/local/zookeeper
配置zookeeper
[root@aiche-node02 tools]# cd /usr/local/zookeeper/conf
[root@aiche-node02 conf]# cp zoo_sample.cfg zoo.cfg
[root@aiche-node02 conf]# vim zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper/data
dataLogDir=/data/zookeeper/log/
clientPort=2181
#参数解释:
#tickTime:客户端与服务器或者服务器与服务器之间维持心跳,也就是每个tickTime时间就会发送一次心跳。通过心跳不仅能够用来监听机器的工作状态,还可以通过心跳来控制Flower跟Leader的通信时间,默认情况下FL的会话时常是心跳间隔的两倍。
#initLimit:集群中的follower服务器(F)与leader服务器(L)之间初始连接时能容忍的最多心跳数(tickTime的数量)
#dataDir:zookeeper服务器存储快照文件的目录
#用于配置zookeeper服务器存储事务日志文件的目录,默认情况会将事务日志文件和快照数据存储在同一个目录中,尽量将两个目录分开如果条件允许,可以将事务日志的存储配置在一个单独的磁盘上,事务日志的记录对磁盘的性能要求非常高,为了保证事务的一致性zookeeper再返回客户端事务请求响应之前必须将本次请求对应的事务日志写入到磁盘中因此事务日志写入的性能直接决定了zookeeper在处理事务请求时的吞吐量针对同一块磁盘的并发读写操作,尤其是数据快照的操作,会极大影响输入日志的写入性能,因此,尽量给事务日志的输出配置单独的磁盘或者挂载点,将极大的提高zookeeper的整体性能
#syncLimit:配置 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 syncLimit * tickTime 秒
#clientPort:这个端口就是客户端连接Zookeeper服务器的端口,Zookeeper会监听这个端口,接受客户端的访问请求。
创建数据和日志目录
mkdir /data/zookeeper/{data,log} -p
启动zookeeper
[root@aiche-node02 conf]# /usr/local/zookeeper/bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
Zookeeper服务常用命令:
启动:/data/zookeeper/bin/zkServer.sh start
关闭:/data/zookeeper/bin/zkServer.sh stop
状态: /data/zookeeper/bin/zkServer.sh status
重启:/data/zookeeper/bin/zkServer.sh restart
三、Zookeeper集群部署
环境,jdk安装,zookeeper部署同上
最少3台,可以在单机上完成,伪集群。
- QQ精品交流群
-
- 微信公众号
-