ZooKeeper应用场景:分布式共享锁

文章转载至:https://www.cnblogs.com/luangeng/p/7398174.html

在java并发包中提供了若干锁的实现,它们是用于单个java虚拟机进程中的;而分布式锁能够在一组进程之间提供互斥机制,保证在任何时刻只有一个进程可以持有锁。

分布式环境中多个进程的锁则可以使用Zookeeper来实现。

下面这种方法是使用顺序节点实现共享锁,流程如下:

对于lock()操作,首先让所有参与争锁的客户端都在/_locks目录下创建临时顺序节点,然后获取该路径下的所有节点,如果客户端创建的节点序列号最小则获得锁。否则开始监视它前一个节点并进入等待状态。

对于unlock()操作,将自身创建的节点删除。此时后一个节点的监控将被触发,对应的客户端退出等待状态,取得锁。

下面是一个简单的示例:

/** * 分布式锁

*/publicclassDisLockimplements Watcher {

    publicstaticfinalString LOCK_ROOT = "/__locks__";

    private ZooKeeper zk;

    //锁名称,标识竞争的是哪个锁private String lockName;

    //当前创建的节点路径private String path;

    //前一个节点的路径private String prePath;

    //是否获取锁privateboolean acquired;

    //构造函数,连接zk,检查父节点存在publicDisLock(String lockName)throws KeeperException, InterruptedException, IOException {

        this.lockName = "/" + lockName;

        this.zk =newZooKeeper("localhost:2181", 30000,this);

        Assert.notNull(zk, "zookeeper is null");

        if(zk.exists(LOCK_ROOT,false) ==null) {

            zk.create(LOCK_ROOT, newbyte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,

                    CreateMode.PERSISTENT);

        }

        if(zk.exists(LOCK_ROOT +this.lockName,false) ==null) {

            zk.create(LOCK_ROOT +this.lockName,newbyte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,

                    CreateMode.PERSISTENT);

        }

    }

    publicvoid lock() {

        if (tryLock()) {

            return;

        } else {

            waitLock();

        }

    }

    //尝试获取锁publicboolean tryLock() {

        if (acquired) {

            returntrue;

        }

        try {

            //创建临时节点,自动编号path = zk.create(LOCK_ROOT + lockName + "/",newbyte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,

                    CreateMode.EPHEMERAL_SEQUENTIAL);

            List ls = zk.getChildren(LOCK_ROOT + lockName,false);

            Collections.sort(ls);

            if(path.equals(LOCK_ROOT + lockName + "/" + ls.get(0))) {

                acquired =true;

                returntrue;

            }

            for(inti = 0; i < ls.size(); i++) {

                if(path.equals(LOCK_ROOT + lockName + "/" + ls.get(i))) {

                    prePath = LOCK_ROOT + lockName + "/" + ls.get(i - 1);

                    break;

                }

            }

            returnfalse;

        } catch (KeeperException e) {

            e.printStackTrace();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        returnfalse;

    }

    //释放锁publicvoid unlock() {

        if(!acquired) {

            return;

        }

        try {

            zk.delete(path, -1);

            acquired =false;

            //System.out.println(Thread.currentThread().getName() + " free lock");}catch (InterruptedException e) {

            e.printStackTrace();

        } catch (KeeperException e) {

            e.printStackTrace();

        }

    }

    //设置监控并等待锁,前一个节点被删除后退出等待,得到锁privatesynchronizedvoid waitLock() {

        try {

            Stat s = zk.exists(prePath,true);

            if(s ==null) {

                //等到锁,返回acquired =true;

                return;

            }

            this.wait();

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (KeeperException e) {

            e.printStackTrace();

        }

        waitLock();

        return;

    }

    //监视节点变化,被监控节点被删除时激活等待锁的线程    @Override

    publicsynchronizedvoid process(WatchedEvent watchedEvent) {

        if(watchedEvent.getType() == Event.EventType.NodeDeleted) {

            //System.out.println("触发:"+watchedEvent.getPath());this.notify();

        }

    }

}

其中waitLock和process方法需要加synchronized关键字,以便使用wait和notify方法。


测试方法:

Zookeeper的另一种应用场景是处理FIFO消息队列,利用zk的自动编号,存储数据和数据一致性能力,模拟生产者-消费者模型。

创建3个消费者从zk中获取数据,此时需要使用分布式锁,加锁获取数据的部分。出于模拟生产者和消费者都在不同的进程,所有不共享zk等对象。

示例代码如下:

生产者:

publicclassProducerextends Thread {

    private ZooKeeper zk;

    privateRandom ran =new Random();

    privatestaticAtomicInteger count =newAtomicInteger(0);

    Producer() throws IOException {

        this.zk =newZooKeeper("localhost:2181", 30000,null);

    }

    voidproduce(String str)throws KeeperException, InterruptedException {

        String name = zk.create(ZkQueue.root + "/element", str.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,

                CreateMode.PERSISTENT_SEQUENTIAL);

        //System.out.println(getName() + " create: " + str);    }

    @Override

    publicvoid run() {

        try {

            while(true) {

                String msg = "msg" + count.getAndIncrement();

                produce(msg);

                Thread.sleep(ran.nextInt(1000));

            }

        } catch (KeeperException e) {

            e.printStackTrace();

        } catch (InterruptedException e) {

            e.printStackTrace();

        } finally {

        }

    }

}

---循环向zk中写入递增的信息,中间延迟随机毫秒


消费者:

publicclassConsumerextends Thread {

    private ZooKeeper zk;

    private DisLock lock;

    Consumer() throwsIOException, KeeperException, InterruptedException {lock = new DisLock("queue");

        this.zk =newZooKeeper("localhost:2181", 30000,null);

    }

    privatebooleanconsume()throwsKeeperException, InterruptedException {lock.lock();

        try {

            List list = zk.getChildren(root,true);

            if (list.isEmpty()) {

                returntrue;

            }

            Collections.sort(list);

            String first = list.get(0);

            byte[] b = zk.getData(root + "/" + first,false,null);

            zk.delete(root + "/" + first, -1);

            String str =new String(b);

            System.out.println(getName() + " get:" + str);

        } finally{lock.unlock();}returnfalse;

    }

    @Override

    publicvoid run() {

        try {

            while(true) {

                if (consume()) {

                    Thread.sleep(1000);

                }

            }

        } catch (KeeperException e) {

            e.printStackTrace();

        } catch (InterruptedException e) {

            e.printStackTrace();

        } finally {

        }

    }

}


---循环从zk中取节点然后删除节点,对整个过程加锁


启动类:

publicclassZkQueueimplements Watcher{

    @Override

    publicvoid process(WatchedEvent watchedEvent) {

        System.out.printf("---->%s %s\n",watchedEvent.getPath(),watchedEvent.getType());

    }

    privatestatic ZooKeeper zk;

    publicstaticfinalString root = "/zkqueue";

    publicstaticvoidmain(String[] args)throws IOException, KeeperException, InterruptedException {

        zk =newZooKeeper("localhost:2181", 30000,null);

        ZkUtils.delete(zk, "/__locks__/queue");//此处需保证该路径下没有子节点,否则可能获取到为最小        ZkUtils.delete(zk, root);

        zk.create(root, "queue".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        //模拟2个生产者new Producer().start();

        new Producer().start();

        //模拟3个消费者new Consumer().start();

        new Consumer().start();

        new Consumer().start();

        Scanner s =new Scanner(System.in);

        s.nextLine();

        ZkUtils.delete(zk, root);

        // 关闭连接        zk.close();

    }

}

你可能感兴趣的:(ZooKeeper应用场景:分布式共享锁)