Zookeeper的几个简单应用

转载于ZooKeeper学习第五期

分布式锁

实现Watcher接口,作为以下几个应用的父类TestMainClient.java。

package com.xcy.ZK;

import org.apache.log4j.xml.DOMConfigurator;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;


public class TestMainClient implements Watcher {
    protected static ZooKeeper zk = null;
    protected static Integer mutex;
    int sessionTimeout = 10000;
    protected String root;
    public TestMainClient(String connectString) {
        if(zk == null){
            try {

                String configFile = this.getClass().getResource("/").getPath()+"org/zk/leader/election/log4j.xml";
                DOMConfigurator.configure(configFile);
                System.out.println("创建一个新的连接:");
                zk = new ZooKeeper(connectString, sessionTimeout, this);
                mutex = new Integer(-1);
            } catch (IOException e) {
                zk = null;
            }
        }
    }
    //synchronized:保证在同一时刻,只有一个线程可以执行某个方法或某个代码块
    synchronized public void process(WatchedEvent event) {
        synchronized (mutex) {
            mutex.notify();
            //唤醒一个等待(对象的)线程并使该线程开始执行
        }
    }
}

Locks.java

package com.xcy.ZK;

import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;


import java.util.Arrays;
import java.util.List;


public class Locks extends TestMainClient {
    public static final Logger logger = Logger.getLogger(Locks.class);
    String myZnode;

    public Locks(String connectString, String root) {
        super(connectString);
        this.root = root;
        if (zk != null) {
            try {
                Stat s = zk.exists(root, false);
                if (s == null) {
                    zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                }
            } catch (KeeperException e) {
                logger.error(e);
            } catch (InterruptedException e) {
                logger.error(e);
            }
        }
    }
    //查看是否拿到锁
    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 check() throws InterruptedException, KeeperException {
        myZnode = zk.create(root + "/lock_" , new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
        getLock();
    }
    //线程等待,等待
    void waitForLock(String lower) throws InterruptedException, KeeperException {
        Stat stat = zk.exists(root + "/" + lower,true);//监控节点,如果拿到最小号节点,就从服务器通知到该线程,执行process
        if(stat != null){
            mutex.wait();
        }
        else{
            getLock();
        }
    }
    @Override
    //确认已经拿到了最小节点
    public void process(WatchedEvent event) {
        if(event.getType() == Event.EventType.NodeDeleted){
            System.out.println("得到通知");
            super.process(event);
            doAction();
        }
    }
    /**
     * 执行其他任务
     */
    private void doAction(){
        System.out.println("同步队列已经得到同步,可以开始执行后面的任务了");
    }
    //每个线程(客户机)都同时执行。
    public static void main(String[] args) {
        String connectString = "localhost:2181";

        Locks lk = new Locks(connectString, "/locks");//创建父节点
        try {
            lk.check();
        } catch (InterruptedException e) {
            logger.error(e);
        } catch (KeeperException e) {
            logger.error(e);
        }
    }
}

Master选举

LeaderElection.java

package com.xcy.ZK;

import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;

import java.net.InetAddress;
import java.net.UnknownHostException;


public class LeaderElection extends TestMainClient {
    public static final Logger logger = Logger.getLogger(LeaderElection.class);

    public LeaderElection(String connectString, String root) {
        super(connectString);
        this.root = root;
        if (zk != null) {
            try {
                Stat s = zk.exists(root, false);
                if (s == null) {
                    zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                }
            } catch (KeeperException e) {
                logger.error(e);
            } catch (InterruptedException e) {
                logger.error(e);
            }
        }
    }

    void findLeader() throws InterruptedException, UnknownHostException, KeeperException {
        byte[] leader = null;
        //判断当前leader是否已被其他线程创建,且向服务器注册watch,若以后有注册就通知改线程。
        try {
            leader = zk.getData(root + "/leader", true, null);
        } catch (KeeperException e) {
            if (e instanceof KeeperException.NoNodeException) {
                logger.error(e);
            } else {
                throw e;
            }
        }
        //当前已被创建,直接作为follower。
        if (leader != null) {
            following();
        } else {
            String newLeader = null;
            byte[] localhost = InetAddress.getLocalHost().getAddress();
            //试图创建leader节点
            try {
                newLeader = zk.create(root + "/leader", localhost, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            } catch (KeeperException e) {
                if (e instanceof KeeperException.NodeExistsException) {
                    logger.error(e);
                } else {
                    throw e;
                }
            }
            //若成功创建后,成为领导者,同时服务器通知到其他wait中的线程启动执行process;否则进入线程等待,给其他线程让位。
            if (newLeader != null) {
                leading();
            } else {
                mutex.wait();
            }
        }
    }

    @Override
    //传来噩耗,被其他线程成功创建了领导者,自己只能作为follower
    public void process(WatchedEvent event) {
        if (event.getPath().equals(root + "/leader") && event.getType() == Event.EventType.NodeCreated) {
            System.out.println("得到通知");
            super.process(event);
            following();
        }
    }

    void leading() {
        System.out.println("成为领导者");
    }

    void following() {
        System.out.println("成为组成员");
    }

    public static void main(String[] args) {
        String connectString = "localhost:2181";

        LeaderElection le = new LeaderElection(connectString, "/GroupMembers");//创建父节点
        try {
            le.findLeader();
        } catch (Exception e) {
            logger.error(e);
        }
    }
}

队列管理

  • 同步队列
    Synchronizing.java
package com.xcy.ZK;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;


public class Synchronizing extends TestMainClient {
    int size;
    String name;
    public static final Logger logger = Logger.getLogger(Synchronizing.class);

    /**
     * 构造函数
     *
     * @param connectString 服务器连接
     * @param root 根目录
     * @param size 队列大小
     */
    Synchronizing(String connectString, String root, int size) {
        super(connectString);
        this.root = root;
        this.size = size;

        if (zk != null) {
            try {
                Stat s = zk.exists(root, false);
                if (s == null) {
                    zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
                }
            } catch (KeeperException e) {
                logger.error(e);
            } catch (InterruptedException e) {
                logger.error(e);
            }
        }
        try {
            name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString());
        } catch (UnknownHostException e) {
            logger.error(e);
        }

    }

    /**
     * 加入队列
     *
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     */

    void addQueue() throws KeeperException, InterruptedException{
        //判断当前start节点是否存在,并注册watch。
        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);
            //若队列成员小于成员总数,线程进入等待;否则可以创建start节点,永久、无序号,同时通知等待中的线程执行process。
            if (list.size() < size) {
                mutex.wait();
            } else {
                zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
            }
        }
    }

    @Override
    //传来命令,最后一个入队列的线程创建了start节点,可以执行任务了。
    public void process(WatchedEvent event) {
        if(event.getPath().equals(root + "/start") && event.getType() == Event.EventType.NodeCreated){
            System.out.println("得到通知");
            super.process(event);
            doAction();
        }
    }

    /**
     * 执行其他任务
     */
    private void doAction(){
        System.out.println("同步队列已经得到同步,可以开始执行后面的任务了");
    }

    public static void main(String args[]) {
        //启动Server
        String connectString = "localhost:2181";
        int size = 1;
        Synchronizing b = new Synchronizing(connectString, "/synchronizing", size);//创建父节点
        try{
            b.addQueue();
        } catch (KeeperException e){
            logger.error(e);
        } catch (InterruptedException e){
            logger.error(e);
        }
    }
}
  • FIFO队列
    FIFOQueue.java
package com.xcy.ZK;

import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;

import java.nio.ByteBuffer;
import java.util.List;


public class FIFOQueue extends TestMainClient{
    public static final Logger logger = Logger.getLogger(FIFOQueue.class);

    /**
     * Constructor
     *
     * @param connectString
     * @param root
     */
    FIFOQueue(String connectString, String root) {
        super(connectString);
        this.root = root;
        if (zk != null) {
            try {
                Stat s = zk.exists(root, false);
                if (s == null) {
                    zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
                }
            } catch (KeeperException e) {
                logger.error(e);
            } catch (InterruptedException e) {
                logger.error(e);
            }
        }
    }
    /**
     * 生产者
     *
     * @param i
     * @return
     */

    boolean produce(int i) throws KeeperException, InterruptedException{
        //将i转化为字节数组
        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;
    }


    /**
     * 消费者
     *
     * @return
     * @throws KeeperException
     * @throws InterruptedException
     */
    int consume() throws KeeperException, InterruptedException{
        int retvalue = -1;
        Stat stat = null;
        //循环执行,保障被唤醒的消费者能继续消费。
        while (true) {
            //保证只有一个线程运行该代码块
            synchronized (mutex) {
                //注册watch,返回值有变化时通知线程苏醒。
                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;
                }
            }
        }
    }

    @Override
    //被通知苏醒
    public void process(WatchedEvent event) {
        super.process(event);
    }

    public static void main(String args[]) {
        //启动Server
        TestMainServer.start();
        String connectString = "localhost:"+TestMainServer.CLIENT_PORT;

        FIFOQueue q = new FIFOQueue(connectString, "/app1");//创建父节点
        int i;
        Integer max = new Integer(5);

        System.out.println("Producer");
        for (i = 0; i < max; i++) {
            try {
                q.produce(10 + i);
            } catch (KeeperException e) {
                logger.error(e);
            } catch (InterruptedException e) {
                logger.error(e);
            }
        }
        for (i = 0; i < max; i++) {
            try{
                int r = q.consume();
                System.out.println("Item: " + r);
            } catch (KeeperException e){
                i--;
                logger.error(e);
            } catch (InterruptedException e){
                logger.error(e);
            }
        }

    }
}

代码来源于转载,博主仅仅加了点注释

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值