Zookeeper入门学习

来源:互联网 发布:瓷砖设计软件 编辑:程序博客网 时间:2024/05/10 15:24

简介

Apache Zookeeper是由 Apache Hadoop Zookeeper子项目发展而来,现在已经成为了 Apache的顶级项目。Zookeeper为分布式系统提供了高效可靠且易于使用的协同服务,它可以为分布式应用提供相当多的服务,诸如统一命名服务,配置管理,状态同步和组服务等。 Zookeeper接口简单,开发人员不必过多地纠结在分布式系统编程难于处理的同步和一致性问题上,你可以使用Zookeeper 提供的现成(off-the-shelf)服务来实现分布式系统的配置管理组管理Leader选举等功能

参考博文地址:http://www.cnblogs.com/haippy/archive/2012/07/20/2600077.html

一个简单的Zookeeper Watch客户端

为了介绍 ZookeeperJava API的基本用法,本文将带你如何一步一步实现一个功能简单的 Zookeeper客户端。该 Zookeeper客户端会监视一个你指定 Zookeeper节点 Znode当被监视的节点发生变化时,客户端会启动或者停止某一程序。

基本要求

该客户端具备四个基本要求:

·        客户端所带参数:

·        Zookeeper服务地址。

·        被监视的 Znode节点名称。

·        可执行程序及其所带的参数

·        客户端会获取被监视 Znode节点的数据并启动你所指定的可执行程序。

·        如果被监视的 Znode节点发生改变,客户端重新获取其内容并再次启动你所指定的可执行程序。

·        如果被监视的 Znode节点消失,客户端会杀死可执行程序。

程序设计

一般而言,Zookeeper应用程序分为两部分,其中一部分维护与服务器端的连接,另外一部分监视Znode节点的数据。在本程序中,Executor 类负责维护 Zookeeper 连接,DataMonitor 类监视 Zookeeper目录树中的数据,同时,Executor包含了主线程和程序主要的执行逻辑,它负责少量的用户交互,以及与可执行程序的交互,该可执行程序接受你向它传入的参数,并且会根据被监视的Znode节点的状态变化停止或重启。

Executor

Executor 对象是本例程最基本的容器,它包括Zookeeper 对象和DataMonitor对象。

<span style="font-size:12px;">public static void main(String[] args) {        if (args.length < 4) {            System.err                    .println("USAGE: Executor hostPort znode filename program [args ...]");            System.exit(2);        }        String hostPort = args[0];        String znode = args[1];        String filename = args[2];        String exec[] = new String[args.length - 3];        System.arraycopy(args, 3, exec, 0, exec.length);        try {            new Executor(hostPort, znode, filename, exec).run();        } catch (Exception e) {            e.printStackTrace();        }    }    public Executor(String hostPort, String znode, String filename,            String exec[]) throws KeeperException, IOException {        this.filename = filename;        this.exec = exec;        zk = new ZooKeeper(hostPort, 3000, this);        dm = new DataMonitor(zk, znode, null, this);    }    public void run() {        try {            synchronized (this) {                while (!dm.dead) {                    wait();                }            }        } catch (InterruptedException e) {        }    }</span>

回忆一下Executor 的任务是根据 Zookeeper Znode节点状态改变所触发的事件来启动和停止你在命令行指定的可执行程序在上面的代码你可以看到,Executor类在其构造函数中实例化 Zookeeper对象时,将其自身的引用作为 Watch参数传递给 Zookeeper的构造函数,同时它也将其自身的引用作为 DataMonitorListener参数传递给 DataMonitor的构造函数Executor本身实现了以下接口

public class Executor implements Watcher, Runnable, DataMonitor.DataMonitorListener {...

Watcher 接口是在ZooKeeper JavaAPI中定义的。 ZooKeeper用它来与容器(此处容器与上面的 Executor 类相似)进行通信,Watcher只支持一个方法,即process(),ZooKeeper用该函数来处理主线程可能感兴趣的事件。例如 Zookeeper 连接或会话的状态,本例中的“容器” Executor只是简单地把事件向下传递给DataMonitor,具体如何处理事件是由 DataMonitor 决定的。本文只是简单地描述了如何使用 Watcher,通常情况下,Executor 或 与 Executor 类似的对象拥有 与Zookeeper 服务端的连接,但它可以将事件传递给其他对象,并有其它的对象处理该事件。

public void process(WatchedEvent event) {        dm.process(event);    }

DataMonitorListener 接口本身不是Zookeeper API的一部分,它完全是一个自定义的接口,可以说是专门为本程序设计的。DataMonitor对象使用该接口和容器(即 Executor 类)进行通信,DataMonitorListener接口如下:

public interface DataMonitorListener {    /**    * The existence status of the node has changed.    */    void exists(byte data[]);    /**    * The ZooKeeper session is no longer valid.    *     * @param rc    * the ZooKeeper reason code    */    void closing(int rc);}

该接口在 DataMonitor中定义,Executor类实现该接口, Executor.exists() 被调用的时候,Executor决定是否启动或停止事先指定的应用程序(回忆一下前文所说的,当 Znode消失时 Zookeeper客户端会杀死该可执行程序)。

 Executor.closing() 被调用的时候,Executor会根据 Zookeeper连接永久性地消失来决定是否关闭自己。

DataMonitor对象根据 Zookeeper状态变化来调用这些方法

以下是Executor类中实现 DataMonitorListener.exists()  DataMonitorListener.closing()的代码

public void exists( byte[] data ) {    if (data == null) {        if (child != null) {            System.out.println("Killing process");            child.destroy();            try {                child.waitFor();            } catch (InterruptedException e) {            }        }        child = null;    } else {        if (child != null) {            System.out.println("Stopping child");            child.destroy();            try {               child.waitFor();            } catch (InterruptedException e) {            e.printStackTrace();            }        }        try {            FileOutputStream fos = new FileOutputStream(filename);            fos.write(data);            fos.close();        } catch (IOException e) {            e.printStackTrace();        }        try {            System.out.println("Starting child");            child = Runtime.getRuntime().exec(exec);            new StreamWriter(child.getInputStream(), System.out);            new StreamWriter(child.getErrorStream(), System.err);        } catch (IOException e) {            e.printStackTrace();        }    }}public void closing(int rc) {    synchronized (this) {        notifyAll();    }}

DataMonitor

DataMonitor类是本程序Zookeeper 逻辑的核心它差不多是异步的,并由事件驱动的。DataMonitor构造函数如下:

public DataMonitor(ZooKeeper zk, String znode, Watcher chainedWatcher,        DataMonitorListener listener) {    this.zk = zk;    this.znode = znode;    this.chainedWatcher = chainedWatcher;    this.listener = listener;        // Get things started by checking if the node exists. We are going    // to be completely event driven    zk.exists(znode, true, this, null);}

调用 ZooKeeper.exists() 检查指定的 Znode是否存在,并设置监视传递自身引用作为回调对象,在某种意义上,在watch触发时就会引起真实的处理流程。

 ZooKeeper.exists()操作在服务器端完成时,ZooKeeperAPI会在客户端调用completion callback

public void processResult(int rc, String path, Object ctx, Stat stat) {    boolean exists;    switch (rc) {    case Code.Ok:        exists = true;        break;    case Code.NoNode:        exists = false;        break;    case Code.SessionExpired:    case Code.NoAuth:        dead = true;        listener.closing(rc);        return;    default:        // Retry errors        zk.exists(znode, true, this, null);        return;    }     byte b[] = null;    if (exists) {        try {            b = zk.getData(znode, false, null);        } catch (KeeperException e) {            // We don't need to worry about recovering now. The watch            // callbacks will kick off any exception handling            e.printStackTrace();        } catch (InterruptedException e) {            return;        }    }         if ((b == null && b != prevData)            || (b != null && !Arrays.equals(prevData, b))) {        listener.exists(b);        prevData = b;    }}

上述代码首先检查 Znode是否存在,以及其他重大的不可恢复的错误。如果文件(或者Znode)存在,它将从 Znode获取数据如果状态发生变化再调用 Executor exists()回调函数。注意,getData函数本省必须要做任何的异常处理,因为本身就有监视可以处理任何错误:如果节点在调用 ZooKeeper.getData() 之前被删除,ZooKeeper.exists() 就会触发回调函数,如果存在通信错误,在连接上的监视会在该连接重建之前触发相应的事件,同时引发相应的处理。

最后,DataMonitor处理监视事件的代码如下:

public void process(WatchedEvent event) {        String path = event.getPath();        if (event.getType() == Event.EventType.None) {            // We are are being told that the state of the            // connection has changed            switch (event.getState()) {            case SyncConnected:                // In this particular example we don't need to do anything                // here - watches are automatically re-registered with                 // server and any watches triggered while the client was                 // disconnected will be delivered (in order of course)                break;            case Expired:                // It's all over                dead = true;                listener.closing(KeeperException.Code.SessionExpired);                break;            }        } else {            if (path != null && path.equals(znode)) {                // Something has changed on the node, let's find out                zk.exists(znode, true, this, null);            }        }        if (chainedWatcher != null) {            chainedWatcher.process(event);        }    }

如果客户端 Zookeeper程序在会话失效时(Expiredevent)重新建立了通信信道(SyncConnected event),所有的会话监视会自动和服务器进行重连(Zookeeper 3.0.0以上版本会重置之前设置的监视).更多编程指南请参见 ZooKeeper Watches DataMonitor获得了指定 Znode的事件后,它将调用 ZooKeeper.exists() 来决定究竟发生了什么。

完整的程序:

package zookeeper;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.PrintStream;import org.apache.zookeeper.WatchedEvent;import org.apache.zookeeper.Watcher;import org.apache.zookeeper.ZooKeeper;/** * @author heyongjian * Executor 的任务是根据 Zookeeper 中 Znode 节点状态改变所触发的事件来启动和停止你在命令行指定的可执行程序 */public class Executor implements Watcher,Runnable,DataMonitorListener{String znode;DataMonitor dm;ZooKeeper zk;String filename;String exec[];Process child;public Executor(String hostPort,String znode, String filename, String[] exec) throws IOException {this.filename = filename;this.exec = exec;zk=new ZooKeeper(hostPort, 3000, this);dm=new DataMonitor(zk, znode, null, this);}public static void main(String[] args){if(args.length<4){System.err.println("USAGE: Executor hostPort znode filename program [args ...]");System.exit(2);}String hostPort=args[0];String znode=args[1];String filename=args[2];String exec[] =new String[args.length-1];System.arraycopy(args, 3, exec, 0, exec.length);try{new Executor(hostPort, znode, filename, exec).run();}catch(Exception e){e.printStackTrace();}}public void run() {try{synchronized (this) {while(!dm.dead){wait();}}}catch (InterruptedException e){}}public void process(WatchedEvent event) {dm.process(event);}public void exists(byte[] data) {if(data==null){if(child!=null){System.out.println("Killing process");child.destroy();try{child.waitFor();}catch(InterruptedException e){e.printStackTrace();}try{System.out.println("Starting child");child=Runtime.getRuntime().exec(exec);new StreamWriter(child.getInputStream(), System.out);new StreamWriter(child.getErrorStream(), System.err);}catch(IOException e){e.printStackTrace();}}}}public void closing(int rc) {synchronized (this) {notifyAll();}}static class StreamWriter extends Thread{OutputStream os;InputStream is;public StreamWriter(InputStream is,OutputStream os) {super();this.os = os;this.is = is;}public void run(){byte[] b=new byte[80];int rc;try{while((rc=is.read(b))>0){os.write(b,0,rc);}}catch(IOException e){}}}}
package zookeeper;public interface DataMonitorListener {/** * The existence status of the node has changed. */void exists(byte data[]);/** * The ZooKeeper session is no longer valid. *  * @param rc * the ZooKeeper reason code */void closing(int rc);}
package zookeeper;import java.util.Arrays;import org.apache.zookeeper.KeeperException;import org.apache.zookeeper.WatchedEvent;import org.apache.zookeeper.Watcher;import org.apache.zookeeper.AsyncCallback.StatCallback;import org.apache.zookeeper.KeeperException.Code;import org.apache.zookeeper.ZooKeeper;import org.apache.zookeeper.data.Stat;/** * @author heyongjian * DataMonitor 类监视 Zookeeper 目录树中的数据 */public class DataMonitor implements Watcher, StatCallback {ZooKeeper zk;String znode;Watcher chainedWatcher;boolean dead;DataMonitorListener listener;byte preData[];public DataMonitor(ZooKeeper zk, String znode, Watcher chainedWatcher,DataMonitorListener listener) {this.zk = zk;this.znode = znode;this.chainedWatcher = chainedWatcher;this.listener = listener;// Get things started by checking if the node exists. We are going// to be completely event driven//调用 ZooKeeper.exists() 检查指定的 Znode 是否存在,并设置监视,传递自身引用作为回调对象zk.exists(znode, true, this, null);}public void process(WatchedEvent event) {String path=event.getPath();if(event.getType()==Event.EventType.None){// We are are being told that the state of the            // connection has changedswitch(event.getState()){case SyncConnected:// In this particular example we don't need to do anything                // here - watches are automatically re-registered with                 // server and any watches triggered while the client was                 // disconnected will be delivered (in order of course)break;case Expired:// It's all overdead=true;listener.closing(KeeperException.Code.SessionExpired);break;}}else{if(path!=null&&path.equals(znode)){// Something has changed on the node, let's find outzk.exists(znode, true,this,null);}}if(chainedWatcher!=null){chainedWatcher.process(event);}}public void processResult(int rc, String path, Object ctx, Stat stat) {boolean exists;switch(rc){case Code.Ok:exists=true;break;case Code.NoNode:exists=false;break;case Code.SessionExpired:case Code.NoAuth:dead=true;listener.closing(rc);return;default:// Retry errorszk.exists(znode, true,this,null);return ;}byte[] b=null;if(exists){try{b=zk.getData(znode, false, null);}catch(KeeperException e){// We don't need to worry about recovering now. The watch                // callbacks will kick off any exception handling}catch(InterruptedException e){return;}}if((b==null&&b!=preData)||(b!=null&&!Arrays.equals(preData, b))){listener.exists(b);preData=b;}}}
上述是博主翻译Apache zookeeper 官网的Java example中的说明文档,讲的很好,持续阅读学习!
0 0
原创粉丝点击