zookeeper学习

主要的理论内容在这里:

http://blog.csdn.net/xinguan1267/article/details/38422149


关键有几个知识点:

1、session:client与server之间的连接,超时

2、znode:zookeeper的文件储存格式(inode23333),数据大小(1M),权限控制(ACL),模式:(持久或者session消失就没了),version,cversion,czxid,mzxid,pzxid

3、基本架构:一个leader,多个follower,Observer

4、读写过程:写过程:2次网络IO,一次硬盘IO,读过程:不需要经过leader

5、特性:写log,线性化,FIFO

6、api说明:


接下来是一个应用zookeeper的小demo


/**
 * Created by wangjian36 on 17/1/19.
 */
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;

/**
 *
 * @author http://blog.csdn.net/java2000_wl 
 * @version <b>1.0</b> 
 */
public class ZookeeperTest {

    private static final int SESSION_TIMEOUT = 30000;
    

    private Watcher watcher =  new Watcher() {

        public void process(WatchedEvent event) {
            System.out.println("process : " + event.getType());
            if(Event.KeeperState.SyncConnected == event.getState()){
                connectedSemaphore.countDown();
            }
        }
    };

    private ZooKeeper zooKeeper;
    private CountDownLatch connectedSemaphore = new CountDownLatch(1);
    /**
     *  连接zookeeper 
     * <br>------------------------------<br> 
     * @throws IOException
     */
    public void connect() throws IOException, InterruptedException {
        zooKeeper  = new ZooKeeper("localhost:2181", SESSION_TIMEOUT, watcher);
        connectedSemaphore.await();
    }
    Semaphore semaphore = new Semaphore(1);
    public void connectWithLock() throws IOException, InterruptedException {
        semaphore.acquire();
        zooKeeper = new ZooKeeper("localhost:2181", SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("connection started");
                semaphore.release();
            }
        });
        semaphore.acquire();
    }

    /**
     *  关闭连接 
     * <br>------------------------------<br> 
     */
    public void close() {
        try {
            zooKeeper.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建一个znode  
     *  1.CreateMode 取值   
     *  PERSISTENT:持久化,这个目录节点存储的数据不会丢失 
     *  PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名; 
     *  EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session过期超时,这种节点会被自动删除 
     *  EPHEMERAL_SEQUENTIAL:临时自动编号节点 
     * <br>------------------------------<br> 
     */
    public void testCreate(String path,String data) throws KeeperException, InterruptedException {
        String result = null;
            result = zooKeeper.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode
                    .PERSISTENT);
        System.out.println("create result : {}"+result);
    }

    /**
     * 删除节点  忽略版本 
     * <br>------------------------------<br> 
     */
    public void testDelete(String path) {
        try {
            zooKeeper.delete(path, -1);
        } catch (Exception e) {
        }
    }

    /**
     *   获取数据 
     * <br>------------------------------<br> 
     */
    public void testGetData(String path) throws KeeperException, InterruptedException {
        String result = null;
        Stat stat = new Stat();
            byte[] bytes = zooKeeper.getData(path, null, stat);
            result = new String(bytes);
        System.out.println("getdata result : "+result+"\nstat_children "+stat.getNumChildren());
    }

    /**
     *   获取数据  设置watch 
     * <br>------------------------------<br> 
     */
    public void testGetDataWatch(String path) {
        String result = null;
        try {
            byte[] bytes = zooKeeper.getData(path, new Watcher() {
                public void process(WatchedEvent event) {
                    System.out.println("testGetDataWatch  watch : {}"+event.getType());
                }
            }, null);
            result = new String(bytes);
        } catch (Exception e) {

        }
        System.out.println("getdata result : {}"+result);


    }

    /**
     *    判断节点是否存在 
     *    设置是否监控这个目录节点,这里的 watcher 是在创建 ZooKeeper实例时指定的 watcher 
     * <br>------------------------------<br> 
     */
    public Stat testExists(String path) {
        Stat stat = null;
        try {
            stat = zooKeeper.exists(path, false);
        } catch (Exception e) {
        }
        System.out.println("exists result : {}"+stat.getCzxid());
        return stat;
    }

    /**
     *     设置对应znode下的数据  ,  -1表示匹配所有版本 
     * <br>------------------------------<br> 
     */
    public void testSetData(String path) {
        Stat stat = null;
        try {
            stat = zooKeeper.setData(path, "testSetData".getBytes(), -1);
        } catch (Exception e) {
        }
        System.out.println("exists result : {}"+ stat.getVersion());
    }

    /**
     *    判断节点是否存在,  
     *    设置是否监控这个目录节点,这里的 watcher 是在创建 ZooKeeper实例时指定的 watcher 
     * <br>------------------------------<br> 
     */
    public void testExistsWatch1(String path) {
        Stat stat = null;
        try {
            stat = zooKeeper.exists(path, true);
        } catch (Exception e) {
        }

        try {
            zooKeeper.delete(path, -1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *    判断节点是否存在,  
     *    设置监控这个目录节点的 Watcher 
     * <br>------------------------------<br> 
     */
    public void testExistsWatch2(String path) {
        Stat stat = null;
        try {
            stat = zooKeeper.exists(path, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    System.out.println("testExistsWatch2  watch : {}"+event.getType());
                }
            });
        } catch (Exception e) {
        }

        // 触发watch 中的process方法   NodeDataChanged  
        try {
            zooKeeper.setData("/zk002", "testExistsWatch2".getBytes(), -1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 不会触发watch 只会触发一次  
        try {
            zooKeeper.delete("/zk002", -1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *  获取指定节点下的子节点 
     * <br>------------------------------<br> 
     */
    public void testGetChild() {
        try {
            zooKeeper.create("/zk/001", "001".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            zooKeeper.create("/zk/002", "002".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

            List<String> list = zooKeeper.getChildren("/zk", true);
            for (String node : list) {
                System.out.println("node {}"+node);
            }
        } catch (Exception e) {
        }
    }

    public static void main(String args[]) throws IOException, KeeperException, InterruptedException {
        ZookeeperTest zookeeperTest = new ZookeeperTest();
        zookeeperTest.connectWithLock();
        //zookeeperTest.testCreate("/java_test/test","Hello_Zookeeper");
        Stat stat = zookeeperTest.testExists("/java_test/test");
        if(stat!=null)
            zookeeperTest.testGetData("/java_test");
    }
}  



这里,需要注意,在连接的时候,需要有一个CountDownLatch的对象,连接完成之后先await(),在watcher里处理的时候,调用countdown(),CountDownLatch对象,new出来的时候带一个数字,调用await()的时候把当先线程阻塞,然后当countDown,里面的counter到0的时候,阻塞的线程才能被唤醒。

之所以必须要加这块东西,主要是因为链接服务器是一个比较慢的过程,如果还没链接到服务器上,结果就要对文件进行操作,那就会导致error,因此必须将链接服务器的函数,在服务器确定能够链接上,返回一个watcher信息的时候,才唤醒链接函数,这样才能保证当connect函数结束的时候,一定链接上了服务器。


类似的同步方法应该都可以的。

我自己拿信号量实现了一个,不过很不优雅。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值