分布式环境中大多数服务是容许部分失败,也容许数据不一致,但有些最基础的服务是须要高可靠性,高一致性的,这些服务是其余分布式服务运转的基础,好比naming service、分布式lock等,这些分布式的基础服务有如下要求:html
对于这种有些挑战CAP原则 的服务该如何设计,是一个挑战,也是一个不错的研究课题,Apache的ZooKeeper也许给了咱们一个不错的答案。ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务, 它暴露了一个简单的原语集,分布式应用程序能够基于它实现同步服务,配置维护和命名服务等。关于ZooKeeper更多信息能够参见 官方文档java
ZooKeeper的基本使用node
搭一个分布式的ZooKeeper环境比较简单,基本步骤以下:算法
1)在各服务器安装 ZooKeepershell
下载ZooKeeper后在各服务器上进行解压便可apache
tar -xzf zookeeper-3.2.2.tar.gzapi
2)配置集群环境服务器
分别各服务器的zookeeper安装目录下建立名为zoo.cfg的配置文件,内容填写以下:数据结构
其中zoo1和zoo2分别对应集群中各服务器的机器名或ip,server.1和server.2中1和2分别对应各服务器的zookeeper id,id的设置方法为在dataDir配置的目录下建立名为myid的文件,并把id做为其文件内容便可,在本例中就分为设置为1和2。其余配置具体含义可见官方文档。框架
3)启动集群环境
分别在各服务器下运行zookeeper启动脚本
/home/admin/zookeeper-3.2.2/bin/zkServer.sh start
4)应用zookeeper
应用zookeeper能够在是shell中执行命令,也能够在java或c中调用程序接口。
在shell中执行命令,可运行如下命令:
bin/zkCli.sh -server 10.20.147.35:2181
其中 10.20.147.35为集群中任一台机器的ip或机器名。执行后可进入zookeeper的操做面板,具体如何操做可见官方文档
在java中经过调用程序接口来应用zookeeper较为复杂一点,须要了解watch和callback等概念,不过试验最简单的CURD倒不须要这些,只须要使用ZooKeeper这个类便可,具体测试代码以下:
以上代码比较简单,查看一下zooKeeper的api doc就知道如何使用了
ZooKeeper的实现机理
ZooKeeper的实现机理是我看过的开源框架中最复杂的,它的解决是分布式环境中的一致性问题,这个场景也决定了其实现的复杂性。看了两三天的源码仍是有些摸不着头脑,有些超出了个人能力,不过经过看文档和其余高人写的文章大体清楚它的原理和基本结构。
1)ZooKeeper的基本原理
ZooKeeper是以Fast Paxos算法为基础的,在前一篇 blog 中大体介绍了一下paxos,而没有提到的是paxos存在活锁的问题,也就是当有多个 proposer交错提交时,有可能互相排斥致使没有一个proposer能提交成功,而Fast Paxos做了一些优化,经过选举产生一个leader,只有leader才能提交propose,具体算法可见Fast Paxos 。所以,要想弄得ZooKeeper首先得对Fast Paxos有所了解。
2)ZooKeeper的基本运转流程
ZooKeeper主要存在如下两个流程:
选举Leader过程当中算法有不少,但要达到的选举标准是一致的:
同步数据这个流程是ZooKeeper的精髓所在,而且就是Fast Paxos算法的具体实现。一个牛人画了一个ZooKeeper数据流动图,比较直观地描述了ZooKeeper是如何同步数据的。
以上两个核心流程我暂时还不能悟透其中的精髓,这也和我尚未彻底理解Fast Paxos算法有关,有待后续深刻学习
ZooKeeper的应用领域
Tim在blog中提到了Paxos所能应用的几个主要场景,包括database replication、naming service、config配置管理、access control list等等,这也是ZooKeeper能够应用的几个主要场景。此外, ZooKeeper官方文档中提到了几个更为基础的分布式应用,这也算是ZooKeeper的妙用吧
1)分布式Barrier
Barrier是一种控制和协调多个任务触发次序的机制,简单说来就是搞个闸门把欲执行的任务给拦住,等全部任务都处于能够执行的状态时,才放开闸门。它的机理能够见下图所示:
在单机上JDK提供了CyclicBarrier这个类来实现这个机制,但在分布式环境中JDK就无能为力了。在分布式里实现Barrer须要高一致性作保障,所以 ZooKeeper能够派上用场,所采起的方案就是用一个Node做为Barrer的实体,须要被Barrer的任务经过调用exists()检测这个Node的存在,当须要打开Barrier的时候,删掉这个Node,ZooKeeper的watch机制会通知到各个任务能够开始执行。
2) 分布式 Queue
与 Barrier相似 分布式环境中 实现Queue也须要高一致性作保障, ZooKeeper提供了一个种简单的方式,ZooKeeper经过一个Node来维护Queue的实体,用其children来存储Queue的内容,而且 ZooKeeper的create方法中提供了顺序递增的模式,会自动地在name后面加上一个递增的数字来插入新元素。能够用其 children来构建一个queue的数据结构,offer的时候使用create,take的时候按照children的顺序删除第一个便可。 ZooKeeper保障了各个server上数据是一致的,所以也就实现了一个 分布式 Queue。take和offer的实例代码以下所示:
3)分布式lock
利用 ZooKeeper实现 分布式lock,主要是经过一个Node来表明一个Lock,当一个client去拿锁的时候,会在这个Node下建立一个自增序列的child,而后经过getChildren()方式来check建立的child是否是最靠前的,若是是则拿到锁,不然就调用exist()来check第二靠前的child,并加上watch来监视。当拿到锁的child执行完后归还锁,归还锁仅仅须要删除本身建立的child,这时watch机制会通知到全部没有拿到锁的client,这些child就会根据前面所讲的拿锁规则来竞争锁。