简介
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper包含一个简单的原语集,提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在$zookeeper_home\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。
功能
- 记录数据
- 支持数据读取
- 支持监听通知机制
特点
简单
Zookeeper的核心是一个精简的文件系统,它支持一些简单的操作和一些抽象操作,例如,排序和通知。
丰富
Zookeeper的原语操作是很丰富的,可实现一些协调数据结构和协议。例如,分布式队列、分布式锁和一组同级别节点中的“领导者选举”。
高可靠
Zookeeper支持集群模式,可以很容易的解决单点故障问题。
松耦合交互
不同进程间的交互不需要了解彼此,甚至可以不必同时存在,某进程在zookeeper中留下消息后,该进程结束后其它进程还可以读这条消息。
资源库
Zookeeper实现了一个关于通用协调模式的开源共享存储库,能使开发者免于编写这类通用协议。
作用
- 分布式系统中的主节点选举! 比如hbase中的老大的产生 Hmaster(HA)
- 分布式系统中的主从节点感知!
- 分布式系统中的配置文件同步!
- 系统服务器的动态上下线感知!!!
- 分布式系统中的分布式锁的实现!分布式中的同一个对象
- 分布式系统中的名称服务!
- 分布式系统中的负载均衡! ...
Zookeeper的功能其实很简单:就是提供协调服务!
协调服务具体来说有三方面:
- 帮使用者存储一些状态信息
- 帮使用者读取一些信息
- 帮使用者监视一些信息的变化,并将变化作为事件通知给使用者
角色
» 领导者(leader),负责进行投票的发起和决议,更新系统状态
» 学习者(learner),包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并想客户端返回结果,在选主过程中参与投票
» Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度
» 客户端(client),请求发起方
Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协
议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者
崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后
,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
• 为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(
proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识
leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的
统治时期。低32位用于递增计数。
• 每个Server在工作过程中有三种状态:
LOOKING:当前Server不知道leader是谁,正在搜寻
LEADING:当前Server即为选举出来的leader
FOLLOWING:leader已经选举出来,当前Server与之同步
其他文档:http://www.cnblogs.com/lpshou/archive/2013/06/14/3136738.html
数据存储
Zookeeper维护一个类似文件系统的数据结构:
每个子目录项如 NameService 都被称作为 znode(目录节点),和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。
有四种类型的znode:
-
PERSISTENT-持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在
-
PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
-
EPHEMERAL-临时目录节点
客户端与zookeeper断开连接后,该节点被删除
-
EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
数据读写
» Zookeeper是一个由多个server组成的集群
» 一个leader,多个follower
» 每个server保存一份数据副本
» 全局数据一致
» 分布式读写
» 更新请求转发,由leader实施
Zookeeper 的保证
» 更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
» 数据更新原子性,一次数据更新要么成功,要么失败
» 全局唯一数据视图,client无论连接到哪个server,数据视图都是一致的
» 实时性,在一定事件范围内,client能读到最新数据
Zookeeper节点数据操作流程
注:1.在Client向Follwer发出一个写的请求
2.Follwer把请求发送给Leader
3.Leader接收到以后开始发起投票并通知Follwer进行投票
4.Follwer把投票结果发送给Leader
5.Leader将结果汇总后如果需要写入,则开始写入同时把写入操作通知给Leader,然后commit;
6.Follwer把请求结果返回给Client
Follower主要有四个功能:
• 1. 向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);
• 2 .接收Leader消息并进行处理;
• 3 .接收Client的请求,如果为写请求,发送给Leader进行投票;
• 4 .返回Client结果。
Follower的消息循环处理如下几种来自Leader的消息:
• 1 .PING消息: 心跳消息;
• 2 .PROPOSAL消息:Leader发起的提案,要求Follower投票;
• 3 .COMMIT消息:服务器端最新一次提案的信息;
• 4 .UPTODATE消息:表明同步完成;
• 5 .REVALIDATE消息:根据Leader的REVALIDATE结果,关闭待revalidate的session还是允许其接受消息;
• 6 .SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。
选举机制
Leader选举过程(以3个节点的集群为例):
注:每个节点的配置文件中都有一个自己的独一无二的id
zookeeper的进程在不同的工作模式下,有不同的通信端口(比如选举时,通过端口3888通信;作为leader或者follower接收客户端请求时通过端口2181;leader和follower之间通信用2888)
注意在zk集群安装的时候 会人为的为每台机器分配一个唯一的id
集群初次启动时的选举流程
- 第一台机器(id=1)启动,发现没有leader,进入投票模式,投自己,并收到自己投这一票,得1票,不能当选leader(当leader的条件是,集群机器数量过半的票数)
- 第2台机器(id=2)启动,发现没有leader,进入投票模式,投自己(因为自己的id>1 收到的另一台机器的票的id)
- 第1台机器收到2的票,发现集群中有一个比自己id大的机器上线了,重新投票,投id=2
- 第2台收到的得票数为2票,过半数,自己当选,切换模式:Leader模式
- 第1台就发现有Leader存在了,自己切换模式:Follower
- 第3台启动,发现有Leader,自动进入Follower状态
如果每个节点是同时启动的zk 同时选举自己 ,同时广播 , 同时获取别人的广播,3机器会当选
集群在运行过程中的选举流程
- 在某个时间点上,id=2机器挂了(leader),别的机器发现没有leader了,全体进入投票模式
- 先投自己,票中会携带(自己的id,自己的数据的版本号)
- 大家都投数据版本最新的节点做leader,如果有多个节点数据版本一样,则从中选id最大的那个作为投票目标!
Zookeeper安装部署
- 上传安装包并解压
tar -zxf zookeeper-3.4.6.tar.gz -C /opt/apps/
修改配置文件
- mv zoo_sample.cfg zoo.cfg
- vi zoo.cfg
|
在各个节点上,手动创建数据存储目录
[root@spark01 apps]# mkdir -p /opt/appdata/zkdata/
[root@spark02 apps]# mkdir -p /opt/appdata/zkdata/
[root@spark02 apps]# mkdir -p /opt/appdata/zkdata/
在各个节点的数据存储目录中,生成一个myid文件,内容为它的id
[root@doit001~]# echo 1 > /usr/apps/zookeeper-3.4.6/zkData/myid
[root@doit002~]# echo 2 > /usr/apps/zookeeper-3.4.6/zkData/myid
[root@doit003~]# echo 3 > /usr/apps/zookeeper-3.4.6/zkData/myid
分发安装包
[root@spark01 apps]# scp -r zookeeper-3.4.6/ spark02:/opt/apps
[root@spark01 apps]# scp -r zookeeper-3.4.6/ spark03:/opt/apps
启动集群
zookeeper没有自带一个批启脚本,只能手动在每一台节点上一个一个地启动
每台机器都执行
bin/zkServer.sh start zk服务启动
bin/zkServer.sh statuszk 查看服务状态
bin/zkServer.sh stop zk停止服务
[root@doit01 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /usr/apps/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: follower
也可以自己开发一个批启动脚本:
#!/bin/bash
for i in 1 2 3
do
ssh linux0${i} "source /etc/profile;/opt/apps/zookeeper-3.4.6/bin/zkServer.sh $1"
done
sleep 2
if [ $1 == start ]
then
for i in {1..3}
do
ssh doit0${i} "source /etc/profile;/opt/apps/zookeeper-3.4.6/bin/zkServer.sh status "
done
fi