ZooKeeper

来源:互联网 发布:u盘raw格式数据恢复 编辑:程序博客网 时间:2024/05/01 12:34

一、ZK简介

   (1)什么是ZK

      

   (2)zk体系架构

   

  (3)数据模型、节点

    

    

Zookeeper 这种数据结构有如下这些特点:

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

      2. znode 可以有子节点目录,并且每个 znode 可以存储数据,注意 EPHEMERAL(短暂的,相对应的就是PERSISTENT持久的) 类型的目录节点不能有子节点目录

          四种类型的节点:

               PERSISTENT:持久化目录节点,这个目录节点存储的数据不会丢失;

               PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名;

               EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session 超时,这种节点会被自动删除;

               EPHEMERAL_SEQUENTIAL:临时自动编号节点

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

      4. znode 可以是临时节点,一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除

      5. znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基于这个特性实现的,后面在典型的应用场景中会有实例介绍

   

  (4)Watches

     


二、ZK流行的应用场景(六大功能)

    1、统一命名服务(Name Service

         分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,既对人友好又不会重复。

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

     2、配置管理(Configuration Management

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

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

    

下面写个程序来演示zk配置管理的功能:

SetConfig.java:

package zookeeper;import java.io.IOException;import org.apache.zookeeper.CreateMode;import org.apache.zookeeper.KeeperException;import org.apache.zookeeper.WatchedEvent;import org.apache.zookeeper.Watcher;import org.apache.zookeeper.ZooDefs.Ids;import org.apache.zookeeper.ZooKeeper;public class SetConfig {public static String url="192.168.8.91:2181";//连接服务器,2181是端口号,服务器会监视此端口private final static String root="/myConf";//数据库连接URL,username,passwordprivate final static String urlNode=root+"/url";private final static String usernameNode=root+"/username";private final static String passwordNode=root+"/password";//在cli界面这样访问权限:addauth digest password /myConfprivate final static String auth_type="digest";private final static String auth_password="password";public static void main(String[] args) throws IOException, InterruptedException, KeeperException{ZooKeeper zk=new ZooKeeper(url, 3000, new Watcher(){@Overridepublic void process(WatchedEvent event) {System.out.println("触发了事件:"+event.getType());}});while(ZooKeeper.States.CONNECTED!=zk.getState()){Thread.sleep(3000);}//加密操作,对一个session下创建的path进行加密,通常和CREATOR_ALL_ACL一起用zk.addAuthInfo(auth_type, auth_password.getBytes());if(zk.exists(root, true)==null)//判断某个path是否存在,并设置是否监控这个目录节点{zk.create(root, "root".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);}if(zk.exists(urlNode, true)==null){zk.create(urlNode, "202.101.1.1".getBytes(),Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);}if(zk.exists(usernameNode, true)==null){zk.create(usernameNode, "wangfan".getBytes(),Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);}if(zk.exists(passwordNode,true)==null){zk.create(passwordNode, "michael".getBytes(),Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);}zk.close();}}


Client.java
package zookeeper;import java.io.IOException;import org.apache.zookeeper.KeeperException;import org.apache.zookeeper.WatchedEvent;import org.apache.zookeeper.Watcher;import org.apache.zookeeper.ZooKeeper;public class Client implements Watcher{public static String url="192.168.8.91:2181";//连接服务器,2181是端口号,服务器会监视此端口    private final static String root="/myConf";//数据库连接URL,username,passwordprivate  String urlNode=root+"/url";private  String usernameNode=root+"/username";private  String passwordNode=root+"/password";private final static String auth_type="digest";private final static String auth_password="password";private String urlString;private String username;private String password;ZooKeeper zk=null;public String getUrlString() {return urlString;}public void setUrlString(String urlString) {this.urlString = urlString;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public void initValue() throws KeeperException, InterruptedException{urlString=new String(zk.getData(urlNode, false, null));//url变化不会触发事件,getData()方法是获取这个path对应的目录节点存储的数据,同时还可以设置是否监控这个目录节点数据的状态username=new String(zk.getData(usernameNode, true, null));password=new String(zk.getData(passwordNode, true, null));}public ZooKeeper getZK() throws IOException, InterruptedException{zk=new ZooKeeper(url,3000,this);zk.addAuthInfo(auth_type, auth_password.getBytes());while(ZooKeeper.States.CONNECTED!=zk.getState()){Thread.sleep(3000);}return zk;}@Overridepublic void process(WatchedEvent event) {// TODO 自动生成的方法存根if(event.getType()==Watcher.Event.EventType.None){System.out.println("连接服务器成功!");}else if(event.getType()==Watcher.Event.EventType.NodeCreated){System.out.println("节点创建成功!");}else if(event.getType()==Watcher.Event.EventType.NodeChildrenChanged){System.out.println("子节点更新成功!");//读取新的配置try {initValue();} catch (KeeperException e) {// TODO 自动生成的 catch 块e.printStackTrace();} catch (InterruptedException e) {// TODO 自动生成的 catch 块e.printStackTrace();}}else if(event.getType()==Watcher.Event.EventType.NodeDataChanged){System.out.println("节点更新成功!");//读取新的配置try {initValue();} catch (KeeperException e) {// TODO 自动生成的 catch 块e.printStackTrace();} catch (InterruptedException e) {// TODO 自动生成的 catch 块e.printStackTrace();}}else if(event.getType()==Watcher.Event.EventType.NodeDeleted){System.out.println("节点删除成功!");}}public static void main(String[] args) throws IOException, InterruptedException, KeeperException{Client zkTest=new Client();ZooKeeper zk=zkTest.getZK();zkTest.initValue();int i=0;while(true){System.out.println(zkTest.getUrlString());System.out.println(zkTest.getUsername());System.out.println(zkTest.getPassword());System.out.println("-----------------------------------------");Thread.sleep(10000);i++;if(i==10)break;}zk.close();}}
运行:

     (1)首先运行setConfig.java,创建节点

     (2)运行Client.java

          打开cli,命令如下,改变username的值:

       
      同时发现客户端也跟着变化:

      

    当改变url的值得时候,发现并没有变化,因为getData的时候并没有监控这个目录节点数据的状态

 

  3、集群管理

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

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

      它们的实现方式都是在 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 容易出现单点故障的问题。



   4、分布式锁

          

 5、队列管理

     Zookeeper 可以处理两种类型的队列:

          1. 当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。

          2. 队列按照 FIFO 方式进行入队和出队操作,例如实现生产者和消费者模型

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

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


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

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


0 0
原创粉丝点击