Zookeeper分布式服务框架例子

由这个定义我们知道zookeeper是个协调系统,作用的对象是分布式系统。为什么分布式系统需要一个协调系统了?理由如下:
开发分布式系统是件很困难的事情,其中的困难主要体现在分布式系统的“部分失败”。“部分失败”是指信息在网络的两个节点之间传送时候,如果网络出了故障,发送者无法知道接收者是否收到了这个信息,而且这种故障的原因很复杂,接收者可能在出现网络错误之前已经收到了信息,也可能没有收到,又或接收者的进程死掉了。发送者能够获得真实情况的唯一办法就是重新连接到接收者,询问接收者错误的原因,这就是分布式系统开发里的“部分失败”问题。

Zookeeper就是解决分布式系统“部分失败”的框架。Zookeeper不是让分布式系统避免“部分失败”问题,而是让分布式系统当碰到部分失败时候,可以正确的处理此类的问题,让分布式系统能正常的运行。

ZooKeeper典型的应用场景
Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似 Master/Slave 管理模式,关于 Zookeeper 的详细架构等内部细节可以阅读 Zookeeper 的源码

下面详细介绍这些典型的应用场景,也就是 Zookeeper 到底能帮我们解决那些问题?下面将给出答案。

统一命名服务(Name Service)
分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,既对人友好又不会重复。说到这里你可能想到了 JNDI,没错 Zookeeper 的 Name Service 与 JNDI 能够完成的功能是差不多的,它们都是将有层次的目录结构关联到一定资源上,但是 Zookeeper 的 Name Service 更加是广泛意义上的关联,也许你并不需要将名称关联到特定资源上,你可能只需要一个不会重复名称,就像数据库中产生一个唯一的数字主键一样。

Name Service已经是Zookeeper 内置的功能,你只要调用 Zookeeper 的 API 就能实现。如调用 create 接口就可以很容易创建一个目录节点。

配置管理(Configuration Management)
配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。

像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。

配置管理结构图
集群管理(Group Membership)
Zookeeper 能够很容易的实现集群管理的功能,如有多台 Server 组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道。

Zookeeper 不仅能够帮你维护当前的集群中机器的服务状态,而且能够帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另一个功能 Leader Election。

它们的实现方式都是在 Zookeeper 上创建一个 EPHEMERAL 类型的目录节点,然后每个 Server 在它们创建目录节点的父目录节点上调用 getChildren(String path, boolean watch) 方法并设置 watch 为 true,由于是 EPHEMERAL 目录节点,当创建它的 Server 死去,这个目录节点也随之被删除,所以 Children 将会变化,这时 getChildren上的 Watch 将会被调用,所以其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是同样的原理。

Zookeeper 如何实现 Leader Election,也就是选出一个 Master Server。和前面的一样每台 Server 创建一个 EPHEMERAL 目录节点,不同的是它还是一个 SEQUENTIAL 目录节点,所以它是个 EPHEMERAL_SEQUENTIAL 目录节点。之所以它是 EPHEMERAL_SEQUENTIAL 目录节点,是因为我们可以给每台 Server 编号,我们可以选择当前是最小编号的 Server 为 Master,假如这个最小编号的 Server 死去,由于是 EPHEMERAL 节点,死去的 Server 对应的节点也被删除,所以当前的节点列表中又出现一个最小编号的节点,我们就选择这个节点为当前 Master。这样就实现了动态选择 Master,避免了传统意义上单 Master 容易出现单点故障的问题。


集群管理结构图
这部分的示例代码如下,完整的代码请看附件:
Leader Election 关键代码

 代码如下 复制代码
void findLeader() throws InterruptedException {
       byte[] leader = null;
       try {
           leader = zk.getData(root + "/leader", true, null);
       } catch (Exception e) {
           logger.error(e);
       }
       if (leader != null) {
           following();
       } else {
           String newLeader = null;
           try {
               byte[] localhost = InetAddress.getLocalHost().getAddress();
               newLeader = zk.create(root + "/leader", localhost,
               ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
           } catch (Exception e) {
               logger.error(e);
           }
           if (newLeader != null) {
               leading();
           } else {
               mutex.wait();
           }
       }
   }

共享锁(Locks)
共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。


Zookeeper 现Locks 流程图
同步锁的实现代码如下,完整的代码请看附件:
同步锁的关键代码

 代码如下 复制代码
void getLock() throws KeeperException, InterruptedException{
       List<String> list = zk.getChildren(root, false);
       String[] nodes = list.toArray(new String[list.size()]);
       Arrays.sort(nodes);
       if(myZnode.equals(root+"/"+nodes[0])){
           doAction();
       }
       else{
           waitForLock(nodes[0]);
       }
   }
   void waitForLock(String lower) throws InterruptedException, KeeperException {
       Stat stat = zk.exists(root + "/" + lower,true);
       if(stat != null){
           mutex.wait();
       }
       else{
           getLock();
       }
   }

队列管理
Zookeeper 可以处理两种类型的队列:
当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。
队列按照 FIFO 方式进行入队和出队操作,例如实现生产者和消费者模型。

同步队列用 Zookeeper 实现的实现思路如下:

创建一个父目录 /synchronizing,每个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,然后每个成员都加入这个队列,加入队列的方式就是创建 /synchronizing/member_i 的临时目录节点,然后每个成员获取 / synchronizing 目录的所有目录节点,也就是 member_i。判断 i 的值是否已经是成员的个数,如果小于成员个数等待 /synchronizing/start 的出现,如果已经相等就创建 /synchronizing/start。

用下面的流程图更容易理解:


同步队列流程图
同步队列的关键代码如下,完整的代码请看附件:
同步队列

 代码如下 复制代码
void addQueue() throws KeeperException, InterruptedException{
       zk.exists(root + "/start",true);
       zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,
       CreateMode.EPHEMERAL_SEQUENTIAL);
       synchronized (mutex) {
           List<String> list = zk.getChildren(root, false);
           if (list.size() < size) {
               mutex.wait();
           } else {
               zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE,
                CreateMode.PERSISTENT);
           }
       }
}

当队列没满是进入 wait(),然后会一直等待 Watch 的通知,Watch 的代码如下:

 代码如下 复制代码
public void process(WatchedEvent event) {
       if(event.getPath().equals(root + "/start") &&
        event.getType() == Event.EventType.NodeCreated){
           System.out.println("得到通知");
           super.process(event);
           doAction();
       }
   }

FIFO 队列用 Zookeeper 实现思路如下:

实现的思路也非常简单,就是在特定的目录下创建 SEQUENTIAL 类型的子目录 /queue_i,这样就能保证所有成员加入队列时都是有编号的,出队列时通过 getChildren( ) 方法可以返回当前所有的队列中的元素,然后消费其中最小的一个,这样就能保证 FIFO。

下面是生产者和消费者这种队列形式的示例代码,完整的代码请看附件:

生产者代码

 代码如下 复制代码
boolean produce(int i) throws KeeperException, InterruptedException{
       ByteBuffer b = ByteBuffer.allocate(4);
       byte[] value;
       b.putInt(i);
       value = b.array();
       zk.create(root + "/element", value, ZooDefs.Ids.OPEN_ACL_UNSAFE,
                   CreateMode.PERSISTENT_SEQUENTIAL);
       return true;
   }

消费者代码

 代码如下 复制代码
int consume() throws KeeperException, InterruptedException{
       int retvalue = -1;
       Stat stat = null;
       while (true) {
           synchronized (mutex) {
               List<String> list = zk.getChildren(root, true);
               if (list.size() == 0) {
                   mutex.wait();
               } else {
                   Integer min = new Integer(list.get(0).substring(7));
                   for(String s : list){
                       Integer tempValue = new Integer(s.substring(7));
                       if(tempValue < min) min = tempValue;
                   }
                   byte[] b = zk.getData(root + "/element" + min,false, stat);
                   zk.delete(root + "/element" + min, 0);
                   ByteBuffer buffer = ByteBuffer.wrap(b);
                   retvalue = buffer.getInt();
                   return retvalue;
               }
           }
       }
}

由此可见zookeeper的特点:

zookeeper是一个精简的文件系统。这点它和hadoop有点像,但是zookeeper这个文件系统是管理小文件的,而hadoop是管理超大文件的。
zookeeper提供了丰富的“构件”,这些构件可以实现很多协调数据结构和协议的操作。例如:分布式队列、分布式锁以及一组同级节点的“领导者选举”算法。
zookeeper是高可用的,它本身的稳定性是相当之好,分布式集群完全可以依赖zookeeper集群的管理,利用zookeeper避免分布式系统的单点故障的问题。
zookeeper采用了松耦合的交互模式。这点在zookeeper提供分布式锁上表现最为明显,zookeeper可以被用作一个约会机制,让参入的
进程不在了解其他进程的(或网络)的情况下能够彼此发现并进行交互,参入的各方甚至不必同时存在,只要在zookeeper留下一条消息,在该进程结束
后,另外一个进程还可以读取这条信息,从而解耦了各个节点之间的关系。
zookeeper为集群提供了一个共享存储库,集群可以从这里集中读写共享的信息,避免了每个节点的共享操作编程,减轻了分布式系统的开发难度。
zookeeper的设计采用的是观察者的设计模式,zookeeper主要是负责存储和管理大家关心的数据,然后接受观察者的注册,一旦这些数据的状态
发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似
Master/Slave 管理模式。
由此可见zookeeper很利于分布式系统开发,它能让分布式系统更加健壮和高效

 

时间: 2024-10-03 01:02:27

Zookeeper分布式服务框架例子的相关文章

Dubbo分布式服务框架入门(附工程)

版权声明:本文为博主原创文章,转载注明出处http://blog.csdn.net/u013142781 目录(?)[+] 要想了解Dubbo是什么,我们不防先了解它有什么用.  使用场景:比如我想开发一个网上商城项目,这个网上商城呢,比较复杂,分为pc端web管理后台,微信端销售公众号,那么我们分成四个项目,pc端网站,微信端网站,还有一个后台服务项目,接口服务项目. 对数据库的操作的相关接口放到接口服务项目,这些接口的实现放在后台服务项目,pc端网站和微信端网站都依赖接口服务项目,调用后台数

基于Spring-DM实现分布式服务框架(DSF)(一)

经过上篇分析分布式服务框架的blog后,正式对之前的基于OSGi实现分布式服务框架的系列改名(顺便把分布式服务框架改为使用DSF缩写),因为已经决定基于Spring-DM来实现,为什么呢,而且为什么一定要是Spring-DM,而不直接说Spring呢? 今天是Spring-DM 1.0 release的大好日子,,不容易呀,做了这么久,具体怎么样还没来得及细看,不过之前有用过1.0 m2,已经觉得很不错了,相信1.0 release更不会失望. 在我眼里看来,Spring是个很大的东西,其实DS

基于OSGi实现分布式服务框架历程(四)

在这个篇幅中将来分析下这个分布式服务框架的服务的生命周期的管理的问题,在分布式服务框架中,支持服务的动态部署.卸载.升级是很关键的,至于服务的生命周期是否需要做到像OSGi那样的动态通知,在这个篇幅中会进行分析,并最终形成这个分布式服务框架的生命周期模型以及到目前为止的服务架构模型. 先来分析下服务的生命周期是否需要做到像OSGi DS的动态通知,这里以服务组件安装为例稍微的说下OSGi DS服务的生命周期模型,在OSGi DS中,当有新的Service Component安装时,首先会检查其是

基于OSGi实现分布式服务框架历程(二)

在这篇历程中来完成对于JINI的Spike,目标仍然是判断基于JINI实现服务的路由.查找需求的满足度. JINI是由Sun研究院制定的,其目标就是为了实现分布式的服务,所以在很多地方可以看到它和分布式服务框架是有不少重叠之处的,来先看看它对于需求的满足度,最后再来分析做个总结. 1.怎么实现远程的将服务注册到服务中心? 在jini中暂时没有找到远程注册服务到服务中心的方法. jini的服务需要和服务中心部署在同一台机器上,这个倒是可以通过服务管理中心直接将sar格式的服务部署上去,支持服务的动

基于OSGi实现分布式服务框架历程(一)

写完之前的那篇基于OSGi实现服务框架的分析后,决定动手来实现一个基于OSGi的分布式服务框架,而其feature呢,就会遵照之前写的服务框架的要素来实现,根据之前的分析,将这个实现过程分为了三个大的步骤来完成:Spike阶段.实现阶段和测试阶段,Spike阶段用于完成几个关键问题的技术的研究和选型:实现阶段用于完成基于OSGi的分布式服务框架:测试阶段用于判断实现的分布式框架对于应用场景的符合程度.性能的情况. 首先进入Spike阶段,在Spike阶段需要完成服务注册.创建以及服务的proxy

分析分布式服务框架

技术是为需求而服务的,分布式服务框架也同样如此,它不是凭空诞生的,也是因为有这样的需求才会有分布式服务框架这么样的东西诞生,在这篇blog中来详细的分析分布式服务框架诞生的原因(其实也是需要用分布式服务框架的应用场景,这里隐含的意思就是并不是什么应用都需要分布式服务框架的).分布式服务框架需要提供的feature以及实现这些feature可选的技术方案. 其实这篇blog应该写在实现分布式服务框架系列blog之前,:),不废话了,来看为什么会需要分布式服务框架,在一个不断发展的大型应用中,系统的

跨语言-能同时支撑多语言互为provider,consumer的分布式服务框架,开源的有吗?

问题描述 能同时支撑多语言互为provider,consumer的分布式服务框架,开源的有吗? 能同时支撑多语言互为provider,consumer的分布式服务框架,开源的有吗? 开源的分布式服务框架(dubbo,HSF等)都不支持跨语言(或许有其他,但是我不知道). 如果没有开源的,我的思路是基于同一种协议(hession,thrift,protobuff,avro等)把各种语言支撑的框架集成到一起(例如 php python c++ 的)形成一个支持多语言互为provider,consum

分布式服务框架 Zookeeper -- 管理分布式环境中的数据

安装和配置详解 本文介绍的 Zookeeper 是以 3.2.2 这个稳定版本为基础,最新的版本可以通过官网 http://hadoop.apache.org/zookeeper/来获取,Zookeeper 的安装非常简单,下面将从单机模式和集群模式两个方面介绍 Zookeeper 的安装和配置. 单机模式 单机安装非常简单,只要获取到 Zookeeper 的压缩包并解压到某个目录如:/home/zookeeper-3.2.2 下,Zookeeper 的启动脚本在 bin 目录下,Linux 下

分布式服务框架 dubbo/dubbox 入门示例(转)

dubbo是一个分布式的服务架构,可直接用于生产环境作为SOA服务框架. 官网首页:http://dubbo.io/ ,官方用户指南 http://dubbo.io/User+Guide-zh.htm上面的几张图画得不错,完全可以当做SOA架构的学习资料 淘宝将这个项目开源出来以后,得到了不少同行的支持,包括: 当当网的扩展版本dubbox :https://github.com/dangdangdotcom/dubbox 京东的扩展版本jd-hydra: http://www.oschina.