2014-01-10---Hadoop的基础学习(七)---Hadoop的组件ZooKeeper
2014-01-10---Hadoop的基础学习(七)---Hadoop的组件ZooKeeper
查封炉台 发表于3年前
2014-01-10---Hadoop的基础学习(七)---Hadoop的组件ZooKeeper
  • 发表于 3年前
  • 阅读 111
  • 收藏 2
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

摘要: Zookeeper 是 Google 的 Chubby一个开源的实现,是 Hadoop 的分布式协调服务。它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等.

1.什么是zookeeper

ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. 

ZooKeeper是有什么用呢? 集中式维护配置信息,命名管理,提供分布式同步,提供组服务.

2.为什么用使用zookeeper

大部分分布式应用需要一个主控、协调器或控制器来管理物理分布的子进程(如资源、任务分配等).目前,大部分应用需要开发私有的协调程序,缺乏一个通用的机制。协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器。zookeeper就是具有通用的机制.

ZooKeeper是一个高可用的分布式数据管理与系统协调框架。基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题。网上对ZK的应用场景也有不少介绍,介绍我用过的zookeeper的场景,其他也不太了解.

a.Hadoop2.0,使用Zookeeper的事件处理确保整个集群只有一个活跃的NameNode,存储配置信息等.

b.HBase,使用Zookeeper的事件处理确保整个集群只有一个HMaster,察觉HRegionServer联机和宕机,存储访问控制列表等.

c.Dubbo,使用zookeeper做注册中心,用来注册dubbo服务.做发布和订阅的用途.

 ZK并非天生就是为这些应用场景设计的,都是后来众多开发者根据其框架的特性,利用其提供的一系列API接口(或者称为原语集),摸索出来的典型使用方法.

3.zookeeper的安装和配置(单机模式)

a.下载资源:http://labs.renren.com/apache-mirror/zookeeper/zookeeper-3.4.3/zookeeper-3.4.3.tar.gz

b.解压资源:tar xzvf zookeeper-3.4.3.tar.gz

c.修改配置文件:一般情况下,

    命令如下:cd zookeeper-3.3.3

         cp conf/zoo_sample.cfg conf/zoo.cfg

                   vi   conf/zoo.cfg

     参数配置如下:

   

d.启动:./bin/zkServer.sh start

e:停止:./bin/zkServer.sh stop

f:测试:

4.zookeeper的安装和配置(集群模式)

a.上传Zookeeper压缩包

b.解压Zookeeper压缩包

c.在某一台机器上(yun10-5),修改配置文件zoo.cfg.

   

d.在(dataDir=/home/lisai/app/zookeeper-3.4.5/data)创建一个myid文件,里面内容是server.N中的N(server.5里面内容为5)命令: echo "5" > myid;

e.将配置好的zk信息拷贝到其他节点,命令:

    scp -r /home/lisai/app/zookeeper-3.4.5/  yun10-6:/home/lisai/app/

    scp -r /home/lisai/app/zookeeper-3.4.5/  yun10-7:/home/lisai/app/

f.SSH登录yun10-6,yun10-7主机。然后修改data下的myid的内容.分别改为6,7.

g.从yun10-5开始依次启动zookeeper: ./bin/zkServer.sh start

集群模式下配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面就有一个数据就是A的值,

Zookeeper启动时会读取这个文件,拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server.

h.测试结果.

5.zookeeper的数据模型

Zookeeper 会维护一个具有层次关系的数据结构,它非常类似于一个标准的文件系统,如图所示:

我们可以总结出来:

1.每个子目录项如 NameService 都被称作为 znode,这个 znode 是被它所在的路径唯一标识,如 Server1这个znode 的标识为/NameService/Server1.

2.znode 可以有子节点目录,并且每个 znode 可以存储数据,注意 EPHEMERAL 类型的目录节点不能有子节点目录.

3.znode 是有版本的,每个 znode 中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据.

4.znode 可以是临时节点,一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除,Zookeeper 的客户端和服务器通信采用长连接方式,每个客户端和服务器通过心跳来保持连接,这个连接状态称为 session,如果 znode 是临时节点,这个 session 失效,znode 也就删除了

5.znode 的目录名可以自动编号,如 App1 已经存在,再创建的话,将会自动命名为 App2

6.znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个是 Zookeeper 的核心特性,Zookeeper的很多功能都是基于这个特性实现的.

6.zookeeper的角色分析

  • 领导者(leader),负责进行投票的发起和决议,更新系统状态

  • 学习者(learner),包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并想客户端返回结果,在选主过程中参与投票

  • Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度

  • 客户端(client),请求发起方.

命令行操作:

7.zookeeper的Java API

   zookeeper的Java编程也不怎么常用,就简单实用基本操作.

 // 创建一个与服务器的连接
 ZooKeeper zk = new ZooKeeper("localhost:" + CLIENT_PORT, 
        ClientBase.CONNECTION_TIMEOUT, new Watcher() { 
            // 监控所有被触发的事件
            public void process(WatchedEvent event) { 
                System.out.println("已经触发了" + event.getType() + "事件!"); 
            } 
        }); 
 // 创建一个目录节点
 zk.create("/testRootPath", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE,
   CreateMode.PERSISTENT); 
 // 创建一个子目录节点
 zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(),
   Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); 
 System.out.println(new String(zk.getData("/testRootPath",false,null))); 
 // 取出子目录节点列表
 System.out.println(zk.getChildren("/testRootPath",true)); 
 // 修改子目录节点数据
 zk.setData("/testRootPath/testChildPathOne","modifyChildDataOne".getBytes(),-1); 
 System.out.println("目录节点状态:["+zk.exists("/testRootPath",true)+"]"); 
 // 创建另外一个子目录节点
 zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(), 
   Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); 
 System.out.println(new String(zk.getData("/testRootPath/testChildPathTwo",true,null))); 
 // 删除子目录节点
 zk.delete("/testRootPath/testChildPathTwo",-1); 
 zk.delete("/testRootPath/testChildPathOne",-1); 
 // 删除父目录节点
 zk.delete("/testRootPath",-1); 
 // 关闭连接
 zk.close();

   输出结果:

已经触发了 None 事件!
 testRootData 
 [testChildPathOne] 
目录节点状态:[5,5,1281804532336,1281804532336,0,1,0,0,12,1,6] 
已经触发了 NodeChildrenChanged 事件!
 testChildDataTwo 
已经触发了 NodeDeleted 事件!
已经触发了 NodeDeleted 事件!

8.zookeeper的工作原理

Zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式和广播模式。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server的完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和server具有相同的系统状态。

一旦leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,发现leader,并和leader进行状态同步。待到同步结束,它也参与消息广播。Zookeeper服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的followers支持。

广播模式需要保证proposal被按顺序处理,因此zk采用了递增的事务id号(zxid)来保证。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64为的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch。低32位是个递增计数。

当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的server都恢复到一个正确的状态。

9.zookeeper的选举过程----》选举之后

选举过程:

1.每个Server启动以后都询问其它的Server它要投票给谁。

2.对于其他server的询问,server每次根据自己的状态都回复自己推荐的leader的id和上一次处理事务的zxid(系统启动时每个server都会推荐自己)

3.收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server

4.计算这过程中获得票数最多的的sever为获胜者,如果获胜者的票数超过半数,则改server被选为leader。否则,继续这个过程,直到leader被选举出来。

选举之后:

1.leader就会开始等待server连接.

2.Follower连接leader,将最大的zxid发送给leader.

3.Leader根据follower的zxid确定同步点.

4.完成同步后通知follower 已经成为uptodate状态.

5Follower收到uptodate消息后,又可以重新接受client的请求进行服务了.

而观察者在其中,是怎么运用的呢?

Observing: 观察状态,这时候observer会观察leader是否有改变,然后同步leader的状态;

Following:  跟随状态,接收leader的proposal ,进行投票。并和leader进行状态同步

Follower: 会去Looking寻找状态,这个状态不知道谁是leader,会发起leader选举;

Observing: 会进行等待.

10.zookeeper的应用场景

a.统一命名服务(类似于JNDI)

Naming Service,命名服务也是分布式系统中比较常见的一类场景。在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的机器,提供的服务地址,远程对象等等—这些我们都可以统称他们为名字(Name)。其中较为常见的就是一些分布式服务框架中的服务地址列表。通过调用ZK提供的创建节点的 API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。

典型案例: 阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表,点击这里查看Dubbo开源项目。在Dubbo实现中:

服务提供者在启动的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。

服务消费者启动的时候,订阅/dubbo/${serviceName}/providers目录下的提供者URL地址, 并向/dubbo/${serviceName} /consumers目录下写入自己的URL地址。

注意,所有向ZK上注册的地址都是临时节点,这样就能够保证服务提供者和消费者能够自动感应资源的变化。

另外,Dubbo还有针对服务粒度的监控,方法是订阅/dubbo/${serviceName}目录下所有提供者和消费者的信息。

b.配置中心

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

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

案例分析:zookeeper很容易实现这种集中式的配置管理,比如将APP1的所有配置配置到/APP1 znode下,APP1所有机器一启动就对/APP1这个节点进行监控(zk.exist(“/APP1″,true)),并且实现回调方法 Watcher,那么在zookeeper上/APP1 znode节点下数据发生变化的时候,每个机器都会收到通知,Watcher方法将会被执行,那么应用再取下数据即可 

(zk.getData(“/APP1″,false,null));

c.集群管理和Master选举

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

案例分析: 建一个EPHEMERAL类型的节点,比如server1创建/APP1SERVERS/SERVER1(可以使用ip,保证不重复),server2创建/APP1SERVERS/SERVER2,然后SERVER1和SERVER2都watch /APP1SERVERS这个父节点,那么也就是这个父节点下数据或者子节点变化都会通知对该节点进行watch的客户端。因为EPHEMERAL类型节 点有一个很重要的特性,就是客户端和服务器端连接断掉或者session过期就会使节点消失,那么在某一个机器挂掉或者断链的时候,其对应的节点就会消 失,然后集群中所有对/APP1SERVERS进行watch的客户端都会收到通知,然后取得最新列表即可。

--------------------------------------------------------------------------------------------------------------------------

不仅能够维护当前的集群中机器的服务状态,而且能够选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另一个功能 Leader Election.一旦master挂掉能够马上能从slave中选出一个master,实现步骤和前者一样,只是机器在启动的 时候在APP1SERVERS创建的节点类型变为EPHEMERAL_SEQUENTIAL类型,这样每个节点会自动被编号,规定编号最小的为master,以当我们对SERVERS节点做监控的时候,得到服务器列表,只要所有集群机器逻辑认为最小编号节点为master,那么master就被选出,而这个master宕机的时候,相应的znode会消失,然后新的服务器列表就被推送到客户端,然后每个节点逻辑认为最小编号节点为master,这样就做到动态master选举..

案例分析:在Hbase中,也是使用ZooKeeper来实现动态HMaster的选举。在Hbase实现中,会在ZK上存储一些ROOT表的地址和 HMaster的地址,HRegionServer也会把自己以临时节点(Ephemeral)的方式注册到Zookeeper中,使得HMaster可以随时感知到各个HRegionServer的存活状态,同时,一旦HMaster出现问题,会重新选举出一个HMaster来运行,从而避免了 HMaster的单点问题.

d.共享锁

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

e.队列管理

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

案例分析:一个大任务Task A,需要在很多子任务完成(或条件就绪)情况下才能进行.

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

------------------------------------------------------------------

ending.

参考链接:http://zookeeper.apache.org/



共有 人打赏支持
粉丝 50
博文 56
码字总数 138491
×
查封炉台
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: