`
zhangwei_david
  • 浏览: 468810 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

ZK实现分布式排它锁

 
阅读更多

   排它锁(Exclusive Locks,简称X锁)又称之为独占锁,是一种基本的锁类型。排他锁的核心就是如何保证仅有一个线程获取到锁,并且在锁释放后,可以及时地通知到其他等待获取锁定的线程。下面使用ZK实现了一个简单的排它锁。

    定义锁

       在ZK下定义一个临时节点节点表示锁

               /**排它锁节点**/
    private final String EXCLUSIVE_LOCK = "/zk-demo/lock";

  获取锁

           在需要获取锁时,所有客户端都需要视图通过调用create()方法在ZK上创建这个临时节点。zk保证所有客户端中仅有一个客户端可以创建成功,如果创建成功的客户端则认为他获取了锁。同时没有获取到则需要向这个节点注册一个监听器,监听其他客户端释放锁。

  

  释放锁

         我们定义的锁是一个临时节点,有两种情况可以释放锁。

  • 当前客户端发生宕机,也就是session断开则这个临时节点被移除。
  • 正常业务逻辑执行完成后主动删除自己创建的临时节点。

       无论在什么情况下移除了lock这个临时节点,ZK都会通知所有在/zk-demo节点上注册的子节点变更监听器。在客户端接收到通知时可以再次发起获取分布式锁的尝试

 

 

/**
 *  分布式锁服务接口,该接口定义了如下功能
 *  <ul>
 *     <li> tryLock 一直等待锁</li>
 *     <li> tryLock 等待一段时间,如果超时则会调用回调方法expire()</li>
 *   </ul>
 *
 * @author zhangwei_david
 * @version $Id: DistributedLockService.java, v 0.1 2015年7月1日 下午9:03:33 zhangwei_david Exp $
 */
public interface DistributedLockService {

    /**
     * 试图获取分布式锁,如果返回true则表示获取了锁
     *
     * @param callback 回调接口
     */
    public void tryLock(CallBack callback);

    /**
     * 视图获取分布式锁,如果在指定timeout时间后容然未能够获取到锁则返回
     *
     * @param callback
     * @param timeout
     */
    public void tryLock(CallBack callback, long timeout);

    /**
     * 回调处理接口
     *
     * @author zhangwei_david
     * @version $Id: DistributedLockService.java, v 0.1 2015年7月2日 上午10:59:22 zhangwei_david Exp $
     */
    public interface CallBack {
        /**
         * 获取分布式锁后回调方法
         */
        public void locked();

        /**
         * 获取分布式锁超时回调方法
         */
        public void expire();
    }

}

 

 

/**
 *   分布式锁服务实现类
 * @author zhangwei_david
 * @version $Id: DistributedLockServiceImpl.java, v 0.1 2015年7月1日 下午9:05:48 zhangwei_david Exp $
 */
@Component
public class DistributedLockServiceImpl implements DistributedLockService {

    private static final String ROOT           = "/zk-demo";

    /**锁的临时节点**/
    private static final String LOCK           = "lock";

    /**排它锁节点**/
    private static final String EXCLUSIVE_LOCK = ROOT + "/" + LOCK;

    private int                 sessionTimeout = 3000;

    /**
     * @see com.david.common.distributedLock.DistributedLockService#tryLock(com.david.common.distributedLock.DistributedLockService.CallBack, long)
     */
    public void tryLock(final CallBack callback, long timeout) {
        try {
            final long expireTime = timeout > 0 ? System.currentTimeMillis() + timeout : -1;
            final ZooKeeper zk = getZooKeeper();
            //向根节点注册一个子节点变化监听器
            List<String> nodes = zk.getChildren(ROOT, new Watcher() {

                public void process(WatchedEvent event) {
                    // 排它锁已经被释放,则视图获取锁
                    if (event.getState() == KeeperState.SyncConnected
                        && event.getType() == EventType.NodeChildrenChanged) {
                        doLock(zk, callback, expireTime);
                    }
                }
            });
            // 没有人获取锁则视图获取锁
            if (!nodes.contains(LOCK)) {
                doLock(zk, callback, expireTime);
            }

        } catch (Exception e) {

        }
    }

    /**
     *
     * @see com.david.common.distributedLock.DistributedLockService#tryLock(com.david.common.distributedLock.DistributedLockService.CallBack)
     */
    public void tryLock(final CallBack callback) {
        tryLock(callback, -1);
    }

    /**
     * 具体执行分布式锁,如果拥有分布式锁则执行callback回调,然后释放锁
     *
     * @param zk
     * @param callback
     * @param expireTime 过期时间
     */
    private void doLock(ZooKeeper zk, CallBack callback, long expireTime) {
        try {
            if (expireTime > 0 && System.currentTimeMillis() > expireTime) {
                callback.expire();
                return;
            }
            String path = zk
                .create(EXCLUSIVE_LOCK, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            System.out.println(path);
            if (path != null) {
                callback.locked();
                zk.delete(EXCLUSIVE_LOCK, -1);
            }
        } catch (Exception e) {

        } finally {
            try {
                zk.close();
            } catch (InterruptedException e) {

            }
        }
    }

    /**
     * 获取ZooKeeper
     *
     * @param sessionTimeout
     * @return
     * @throws Exception
     */
    private ZooKeeper getZooKeeper() throws Exception {
        final CountDownLatch latch = new CountDownLatch(1);
        ZooKeeper zk = new ZooKeeper("localhost:2181", sessionTimeout, new Watcher() {

            public void process(WatchedEvent event) {
                if (KeeperState.SyncConnected == event.getState()) {
                    //如果客户端已经建立连接闭锁减一
                    latch.countDown();
                }
            }
        });
        // 等待连接建立
        latch.await();
        return zk;
    }

    /**
     * Getter method for property <tt>sessionTimeout</tt>.
     *
     * @return property value of sessionTimeout
     */
    public int getSessionTimeout() {
        return sessionTimeout;
    }

    /**
     * Setter method for property <tt>sessionTimeout</tt>.
     *
     * @param sessionTimeout value to be assigned to property sessionTimeout
     */
    public void setSessionTimeout(int sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

}

 

 

6
4
分享到:
评论
5 楼 yjy1304 2016-12-01  
List<String> nodes = zk.getChildren(ROOT, new Watcher(){...})
这个对子节点的变化加了个watcher,若果其他的锁也在这个/zk-demo/下操作子节点也会引起这个watcher的process,是不是会有问题
4 楼 newgo333 2015-07-03  
不错
3 楼 zhangwei_david 2015-07-02  
string2020 写道
分布式锁有啥用?


本质上还是一个锁,它的作用就和JDK的Lock一样;只不过是分布式锁是解决分布式环境下问题
2 楼 string2020 2015-07-02  
楼主,能不能帮忙看看这个和zk有关的问题:
http://www.oschina.net/question/1756518_241782
1 楼 string2020 2015-07-02  
分布式锁有啥用?

相关推荐

Global site tag (gtag.js) - Google Analytics