当前位置 博文首页 > 外星喵的博客:Zookeeper原理与实战
配置管理:在我们的应用中除了代码外,还有各种配置文件,比如数据库连接等。如果我们配置非常多,有很多服务器都需要这个配置,而且还还需要经常去修改,这个时候往往需要寻找一种集中管理配置的服务。Zookeeper将配置信息放它的znode 下,当有配置发生改变时,也就是znode发生变化时,可以通过改变它某个目录节点的内容,并利用watcher通知给各个客户端,从而更改配置。
名字服务:为了通过网络访问一个系统,我们得知道对方的IP地址,但是IP地址对人非常不友好,特别是在我们的服务特别多的时候,如果我们在本地保存服务的地址的时候将非常不方便,但是如果我们只需要访问一个统一的入口,那么维护起来将方便得多了。Zookeeper通过创建一个全局的路径,即是唯一的路径作为一个名字,指向集群中提供的服务的地址,或者一个远程的对象等。
分布式锁:在一个分布式环境中,为了提高可靠性,我们的集群的每台服务器上都部署着同样的服务。但是,如果集群中的每个服务器使用相同的资源的话,那相互之间就要协调,而如果我们只让一个服务进行操作,那又存在单点,就可以使用分布式锁,在某个时刻只让一个服务去使用共享资源。zookeeper上的一个znode可以看作是一把锁,通过createznode的方式来实现。所有客户端都去创建/distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock节点就释放出锁。
集群管理:在分布式的集群中,经常会由于硬件故障,软件故障,网络抖动等问题,有些节点会时常上下线切换。这个时候,集群中其他机器需要感知到这种变化,然后根据这种变化做出对应的决策。client端会对Zookeeper某个znode注册一个watcher监听事件,当该znode发生变化时,这些client会收到Zookeeper的通知,然后client可以根据znode变化来做出业务上的改变等。
Zookeeper采用了 递增的事务Id 来标识,所有的proposal(提议)都在被提出的时候加上了 zxid ,
zxid实际上是一个64位的数字,高32位是 epoch用来标识leader是否发生改变,如果有新的leader产生出来,epoch会自增,低32位用来递增计数。当新产生 proposal的时候,会依据数据库的两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会 开始执行。
当leader崩溃或者leader失去大多数的follower,这时zookeeper进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。
Zk的选举算法有两种,一种是基于basic paxos 实现的,另外一种是基于 fast paxos 算法实现的。系统默认的选举算法为fast paxos 。
Zookeeper 选主流程(fast paxos) fast paxos流程是在选举过程中,某Server首先向所有Server提议自己要 成为leader,当其它Server收到提议以后,解决epoch和zxid的冲突, 并接受对方的提议,然后向对方发送接受提议完成的消息,重复这个流 程,就一定能选举出Leader。
Zookeeper使用了Zab协议来保证各个Server之间的同步。
Zab协议有两种模式,它们分别是 **恢复模式(选主) **和广播模式(同步)。
当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server 具有相同的系统状态。
Zookeeper就状态同步过程如下:
ZooKeeper支持一种Watch操作,Client可以在某个ZNode上设置一个Watcher,来Watch该ZNode上的变化。如果该ZNode上有相应的变化,就会触发这个Watcher,把相应的事件通知给设置Watcher的Client。需要注意的是,ZooKeeper中的Watcher是一次性的,即触发一次就会被取消,如果想继续Watch的话,需要客户端重新设置Watcher。
watch机制的特点:
一次性触发数据发生改变时,一个watcher event会被发送到client, 但是client只会收到一次这样的信息。
watcher event异步发送watcher的通知事件从server发送到client是 异步的,这就存在一个问题,不同的客户端和服务器之间通过socket进 行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件, 由于Zookeeper本身提供了 ordering guarantee,即客户端监听事件后, 才会感知它所监视znode发生了变化。所以我们使用Zookeeper不能期 望能够监控到节点每次的变化。Zookeeper只能保证最终的一致性,而无 法保证强一致性。
数据监视Zookeeper有数据监视和子数据监视getdata() and exists()设置数据监视,getchildren()设置了子节点监视。
setData()执行成功则会触发znode上设置的data watch。一个 成功的create()操作会触发被创建的znode上的数据watch,以及其父节 点上的child watch。而一个成功的delete。操作将会同时触发一个znode 的data watch和child watch(因为这样就没有子节点了),同时也会触发其 父节点的child watch。
当一个客户端连接到一个新的服务器上时,watch将会被以任意会话 事件触发。当与一个服务器失去连接的时候,是无法接收到watch的。而 当client重新连接时,如果需要的话,所有先前注册过的watch,都会被 重新注册。通常这是完全透明的。只有在一个特殊情况下,watch可能会 丢失:对于一个未创建的znode的exist watch,如果在客户端断开连接期 间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个 watch事件可能会被丢失。
Watch是轻量级的,其实就是本地JVM的Callback,服务器端只是存 了是否有设置了 Watcher的布尔类型
Zookeeper作为一个集群提供一致的数据服务,自然,它要在所有机器间 做数据复制。
数据复制的好处:
容错:一个节点出错,不致于让整个系统停止工作,别的节点可以接管它的工作;
提高系统的扩展能力:把负载分布到多个节点上,或者增加节点来提高系统的负载能力;
提高性能:让客户端本地访问就近的节点,提高用户访问速度。
从客户端读写访问的透明度来看,数据复制集群系统分下面两种:
写主(WriteMaster):对数据的修改提交给指定的节点。读无此限制,可 以读取任何一个节点。这种情况下客户端需要对读与写进行区别,俗称读 写分离;
写任意(Write Any):对数据的修改可提交给任意的节点,跟读一样。这 种情况下,客户端对集群节点的角色与变化透明。
对Zookeeper来说,它采用的方式是写任意。通过增加机器,它的读吞吐 能力和响应能力扩展性非常好,而写,随着机器的增多吞吐能力肯定下降(这也是它建立observer的原因),而响应能力则取决于具体实现方式,是延迟复制保持最终一致性,还是立即复制快速响应。
ZooKeeper Client Library提供了丰富直观的API供用户程序使用,下面是一些常用的API:
下载
下载地址:https://zookeeper.apache.org/releases.html#download
解压缩(我是放在/usr/local/zk目录下)
tar -zxvf apache-zookeeper-3.6.3-bin.tar.gz
修改zookeeper配置
#zookeeper根目录改个名字
mv apache-zookeeper-3.6.3-bin zookeeper
#进入conf目录下
cd zookeeper/conf
#zookeeper的配置文件zoo.cfg,可复制conf/zoo_sample.cfg
cp zoo_sample.cfg zoo.cfg
vim zoo.cfg
#修改内容
dataDir=/usr/local/zk/zookeeper/data
dataLogDir=/usr/local/zk/zookeeper/log
#添加内容(节点地址:同步端口:选举端口)
server.1=192.168.124.18:2881:3881
server.2=192.168.124.18:2882:3882
server.3=192.168.124.18:2883:3883
配置环境变量
vi /etc/profile
#添加以下内容
#export ZOOKEEPER=/usr/local/zk/zookeeper
#export PATH=$PATH:$ZOOKEEPER/bin
#使其生效
source /etc/profile
创建myid文件
mkdir -p /usr/local/zk/zookeeper/zkdata
cd /usr/local/zk/zookeeper/zkdata
echo 1 > myid
分发安装包到其他机器(我这里在单机模拟集群)
cp -r zookeeper zookeeper2
cp -r zookeeper zookeeper3
修改其他机器的配置文件
在zookeeper2上:
vim /usr/local/zk/zookeeper2/zkdata/myid
修改myid为:2
在zookeeper3上:
vim /usr/local/zk/zookeeper2/zkdata/myid
修改myid为:3
启动zookeeper
zkServer.sh start start-foreground
查看运行状态
zkServer.sh status
关闭Zookeeper服务
zkServer.sh stop