Zookeeper 实现分布式锁服务

第一篇文章, 有错误还请指正

本篇主要简单介绍基于 zookeeper 3.4的 临时顺序节点, 以一个简单的demo演示分布式锁的获取, 直接贴代码, 代码里边有我的注释.

修改下server地址和端口可直接跑. 核心在 lock() 方法.

maven :

<dependency>
  <groupId>org.apache.zookeeper</groupId>
  <artifactId>zookeeper</artifactId>
  <version>3.4.5</version>
</dependency>

过程如下:

1. 创建临时顺序节点

2. 获得节点所在父节点所有的子节点

3. 根据节点顺序排序, 获得最小顺序节点

4. 和当前节点比较, 如果等于当前节点, 则获得锁

5. 如果不等于, 则获得当前节点的前一个节点, 并注册监听

6. 前一节点如果不存在, 则获得锁

7. 等待前一节点释放锁

8. 随后关闭客户端,释放锁.

package com.citi.csl.herman.zookeeper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

//zoo.cfg
//clientPort=2181
//server.1=168.72.230.27:2888:3888
//server.2=168.72.231.24:2888:3888
//server.3=10.116.47.97:2888:3888
class ZKServerConfig {
    // ip + clientPort
    private String zkServer = "168.72.230.27:2181,168.72.231.24:2181,10.116.47.97:2181";
    private int sessionTimeout = 30000;// ms

    public ZKServerConfig() {}
    public String getServers() {return zkServer;}
    public int getSessionTimeout() {return sessionTimeout;}
}

class ZKHelper {
    // wait until session is available.
    // connection is async, so when new Zookeeper() returned, just complete user
    // config, session is in 'CONNECTING' status.
    public static ZooKeeper getZookKeeper(ZKServerConfig config)
            throws IOException, InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        ZooKeeper zk = new ZooKeeper(config.getServers(),
                config.getSessionTimeout(), new SessionReadyWatcher(
                        countDownLatch));
        countDownLatch.await();
        return zk;
    }

}

// sync helper
class SessionReadyWatcher implements Watcher {
    CountDownLatch countDownLatch;

    SessionReadyWatcher(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    public void process(WatchedEvent event) {
        if (KeeperState.SyncConnected == event.getState())
            countDownLatch.countDown();
        if (EventType.NodeDeleted == event.getType())
            countDownLatch.countDown();
    }
}

public class DistributedLock implements Lock {

    // test 
    public static void main(String[] args) throws InterruptedException {
        final String lockName = "distribution-lock";
        final CountDownLatch countDownLatch = new CountDownLatch(10);
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.submit(new Runnable() {
                public void run() {
                    try {
                        DistributedLock lock = new DistributedLock(lockName);
                        System.out.println(Thread.currentThread().getName() + " connectted to server.");
                        System.out.println(Thread.currentThread().getName() + " try to get lock.");
                        lock.lock();
                        System.out.println(Thread.currentThread().getName() + " get the lock.");
                        // hold the lock a while
                        Thread.sleep(5000);
                        lock.unlock();
                        System.out.println(Thread.currentThread().getName() + " release the lock.");
                        countDownLatch.countDown();
                    } catch (Exception e) {
                    }
                }
            });
        }
        //wait all lock release
        countDownLatch.await();
        //shutdown executors
        executorService.shutdown();
    }

    private ZooKeeper zk;
    // root lock path
    private String app_root = "/zk-herman";
    // lock resources
    private String lockName = "";
    // current lock name
    private String current_lock;
    // lock name before current lock
    private String pre_lock;

    private static final String ZK_PATH_SEPARATOR = "/";

    public DistributedLock(String lockName) {
        try {
            // connect to zk
            zk = ZKHelper.getZookKeeper(new ZKServerConfig());
            System.out.println("connected to zk.");
            this.lockName = lockName;
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    public void lock() {
        try {
            // create ephemeral sequential node
            current_lock = zk.create(app_root + ZK_PATH_SEPARATOR + lockName,
                    null, ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.println(current_lock + " has been set up");
            // get all child nodes
            List<String> subNodes = zk.getChildren(app_root, false);
            List<String> lockObjs = new ArrayList<String>(subNodes.size());
            // get all waiting locks
            for (String subNode : subNodes) {
                if (subNode.startsWith(lockName))
                    lockObjs.add(subNode);
            }
            // sort
            Collections.sort(lockObjs);
            // if current is minimum, then get the lock
            if (current_lock.equals(app_root + "/" + lockObjs.get(0))) {
                return;
            }

            // if not, get previous waiting lock
            String current_lock_name = current_lock.substring(current_lock
                    .lastIndexOf(ZK_PATH_SEPARATOR)+1);
            pre_lock = lockObjs.get(Collections.binarySearch(lockObjs,
                    current_lock_name) - 1);

            // wait previous lock unlock
            CountDownLatch countDownLatch = new CountDownLatch(1);
            // re-check whether previous lock exist, register watcher to listen
            // node change event
            Stat stat = zk.exists(app_root + "/" + pre_lock, new SessionReadyWatcher(
                    countDownLatch));
            // if not exist
            if (stat == null)
                return;
            // wait previous get lock
            countDownLatch.await();
            return;

        } catch (Exception e) {

        }
    }

    // close connection then session closed, the ephemeral znode be deleted
    // automatically
    public void unlock() {
        try {
            zk.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public boolean tryLock() {
        return false;
    }

    public void lockInterruptibly() throws InterruptedException {
    }

    public boolean tryLock(long time, TimeUnit unit)
            throws InterruptedException {
        return false;
    }

    public Condition newCondition() {
        return null;
    }

}

 

posted @ 2018-08-17 17:27  爱笑的咖啡  阅读(278)  评论(0编辑  收藏  举报