• 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;
        }
    
    }
    

      

     
  • 相关阅读:
    Xcode下 gdb 调试命令
    Xcode 获取本地IP
    Linux终端提示符PS1设置(颜色)
    linux 客户端 Socket 非阻塞connect编程
    关于SIGPIPE导致的程序退出
    jQuery MiniUI开发系列之:数据验证
    支持Java Spring MVC
    支持ASP.NET MVC
    支持ASP.NET WebService
    jQuery MiniUI开发系列之:Ajax处理超时、服务端错误
  • 原文地址:https://www.cnblogs.com/wei-zw/p/8797758.html
Copyright © 2020-2023  润新知