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

}

 

 

### Zookeeper 分布式实现原理 Zookeeper 是一种高效的协调服务工具,能够帮助开发者在分布式环境中管理配置、同步状态以及提供一致性保障。通过其核心特性和数据模型,Zookeeper 可以被用来实现分布式。 #### 1. 核心特性支持 Zookeeper 提供了两种类型的节点:**持久节点**和**临时节点**,同时还支持创建有序节点(sequential nodes)。这些特性共同构成了分布式的基础[^3]。 - **临时节点**:当客户端断开连接时,该节点会自动删除。这一特性确保了即使某个进程崩溃或网络中断,也能被安全释放。 - **有序节点**:每次创建新节点时都会附加一个递增的序列号,这使得多个竞争者可以通过比较序列号来判断谁拥有。 #### 2. 的获取流程 为了获得,客户端会在指定路径下创建一个带有 `EPHEMERAL_SEQUENTIAL` 属性的子节点。随后,它会读取当前路径下的所有子节点并按序排列。如果发现自己创建的节点是最小的一个,则表示成功获得了;否则需要监听前驱节点的变化事件[^4]。 一旦前驱节点消失(即持有的客户端主动释放或者因异常退出),当前等待中的客户端会被触发回调函数,并重新尝试获取[^1]。 #### 3. 的释放流程 释放的过程相对简单,只需调用 API 删除之前创建的那个特定子节点即可。由于这是个原子操作,在正常情况下不会引发任何竞态条件问题[^2]。 #### 4. 异常处理机制 考虑到实际运行环境可能存在各种不确定性因素,因此还需要设计合理的错误恢复策略。例如,对于会话超时导致的数据丢失情况,应用程序应该有能力检测到这种状况并对受影响的操作采取补偿措施。 ```java // Java 示例代码展示如何使用 Curator Framework 来简化 zk 客户端编程过程 import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; public class DistributedLockExample { public static void main(String[] args) throws Exception { CuratorFramework client = ...; // 初始化 curator 客户端 InterProcessMutex lock = new InterProcessMutex(client, "/lock_path"); try { lock.acquire(); // 获取 System.out.println("Lock acquired."); // 执行业务逻辑 } finally { lock.release(); // 确保最终总是要释放 System.out.println("Lock released."); } } } ``` 以上就是基于 zookeeper 实现分布式的主要思路及其具体步骤描述。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值