All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock.DistributedLock Maven / Gradle / Ivy

/*
 * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock;

import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock;

import org.I0Itec.zkclient.IZkConnection;
import org.I0Itec.zkclient.exception.ZkException;
import org.I0Itec.zkclient.exception.ZkInterruptedException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.NestableRuntimeException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.otter.shared.arbitrate.impl.zookeeper.AsyncWatcher;
import com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;
import com.alibaba.otter.shared.common.utils.lock.BooleanMutex;
import com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;
import com.alibaba.otter.shared.common.utils.zookeeper.ZooKeeperx;

/**
 * 实现一个基于zookeeper的分布式锁 
* document :
* http://zookeeper.apache.org/doc/trunk/recipes.html * *
 * 使用注意:
 *  传统的{@linkplain ReentrantLock}使用有所区别,ReentrantLock主要用于空只单进程多线程之间的调度,所以要求每个线程使用同一个ReentrantLock实例
 *  而{@linkplain DistributedLock}主要是用于控制多进程的调度,所以如果需要被用来控制多线程时,需要使用不同的DistributedLock实例对象。
 *  因此单个DistributedLock实例在多个线程中进行lock/unlock操作时会有线程安全问题!!
 *  
 * 使用例子:
 * 
 *         DistributedLock lock = new DistributedLock("/lock/");
 *         try {
 *             lock.lock();
 *             // do something
 *         } catch (InterruptedException e1) {
 *             // 可中断
 *         } catch (KeeperException e1) {
 *             // zookeeper异常
 *         } finally {
 *             try {
 *                 lock.unlock();
 *             } catch (KeeperException e) {
 *                 // zookeeper异常
 *             }
 *         }
 * 
 * 
* * @author jianghang 2011-9-29 上午11:16:07 * @version 4.0.0 */ public class DistributedLock { private static final Logger logger = LoggerFactory.getLogger(DistributedLock.class); private static final byte[] data = { 0x12, 0x34 }; // private static final Long DEFAULT_TIMEOUT_PERIOD = 60 * 1000L; private ZkClientx zookeeper = ZooKeeperClient.getInstance(); private final String root; // 根节点路径 private String id; private LockNode idName; private String ownerId; private String lastChildId; private Throwable other = null; private KeeperException exception = null; private InterruptedException interrupt = null; public DistributedLock(String root){ this.root = root; ensureExists(root); } /** * 尝试获取锁操作,阻塞式可被中断 */ public void lock() throws InterruptedException, KeeperException { // 可能初始化的时候就失败了 if (exception != null) { throw exception; } if (interrupt != null) { throw interrupt; } if (other != null) { throw new NestableRuntimeException(other); } if (isOwner()) {// 锁重入 return; } BooleanMutex mutex = new BooleanMutex(); acquireLock(mutex); mutex.get(); // 避免zookeeper重启后导致watcher丢失,会出现死锁使用了超时进行重试 // try { // mutex.get(DEFAULT_TIMEOUT_PERIOD, TimeUnit.MILLISECONDS);// 阻塞等待值为true // } catch (TimeoutException e) { // if (!mutex.state()) { // lock(); // } // } if (exception != null) { unlock(); throw exception; } if (interrupt != null) { unlock(); throw interrupt; } if (other != null) { unlock(); throw new NestableRuntimeException(other); } } /** * 尝试获取锁对象, 不会阻塞 * * @throws InterruptedException * @throws KeeperException */ public boolean tryLock() throws KeeperException { // 可能初始化的时候就失败了 if (exception != null) { throw exception; } if (isOwner()) {// 锁重入 return true; } acquireLock(null); if (exception != null) { unlock(); throw exception; } if (interrupt != null) { unlock(); Thread.currentThread().interrupt(); } if (other != null) { unlock(); throw new NestableRuntimeException(other); } return isOwner(); } /** * 释放锁对象 */ public void unlock() throws KeeperException { if (id != null) { zookeeper.delete(root + "/" + id); id = null; idName = null; } else { // do nothing } } private void ensureExists(final String path) { try { if (zookeeper.exists(path)) { return; } zookeeper.create(path, data, CreateMode.PERSISTENT); } catch (ZkInterruptedException e) { Thread.currentThread().interrupt(); interrupt = (InterruptedException) e.getCause(); } catch (ZkException e) { exception = (KeeperException) e.getCause(); } } /** * 返回锁对象对应的path */ public String getRoot() { return root; } /** * 判断当前是不是锁的owner */ public boolean isOwner() { return id != null && ownerId != null && id.equals(ownerId); } /** * 返回当前的节点id */ public String getId() { return this.id; } // ===================== helper method ============================= /** * 执行lock操作,允许传递watch变量控制是否需要阻塞lock操作 */ private Boolean acquireLock(final BooleanMutex mutex) { try { do { if (id == null) {// 构建当前lock的唯一标识 long sessionId = getSessionId(); String prefix = "x-" + sessionId + "-"; // 如果第一次,则创建一个节点 String path = zookeeper.create(root + "/" + prefix, data, CreateMode.EPHEMERAL_SEQUENTIAL); int index = path.lastIndexOf("/"); id = StringUtils.substring(path, index + 1); idName = new LockNode(id); } if (id != null) { List names = zookeeper.getChildren(root); if (names.isEmpty()) { logger.warn("lock lost with scene:empty list, id[] and node[]", id, idName); unlock();// 异常情况,退出后重新创建一个 } else { // 对节点进行排序 SortedSet sortedNames = new TreeSet(); for (String name : names) { sortedNames.add(new LockNode(name)); } if (sortedNames.contains(idName) == false) { logger.warn("lock lost with scene:not contains ,id[] and node[]", id, idName); unlock();// 异常情况,退出后重新创建一个 continue; } // 将第一个节点做为ownerId ownerId = sortedNames.first().getName(); if (mutex != null && isOwner()) { mutex.set(true);// 直接更新状态,返回 return true; } else if (mutex == null) { return isOwner(); } SortedSet lessThanMe = sortedNames.headSet(idName); if (!lessThanMe.isEmpty()) { // 关注一下排队在自己之前的最近的一个节点 LockNode lastChildName = lessThanMe.last(); lastChildId = lastChildName.getName(); // 异步watcher处理 IZkConnection connection = zookeeper.getConnection(); // zkclient包装的是一个持久化的zk,分布式lock只需要一次性的watcher,需要调用原始的zk链接进行操作 ZooKeeper orginZk = ((ZooKeeperx) connection).getZookeeper(); Stat stat = orginZk.exists(root + "/" + lastChildId, new AsyncWatcher() { public void asyncProcess(WatchedEvent event) { if (!mutex.state()) { // 避免重复获取lock acquireLock(mutex); } else { logger.warn("locked successful."); } } }); if (stat == null) { acquireLock(mutex);// 如果节点不存在,需要自己重新触发一下,watcher不会被挂上去 } } else { if (isOwner()) { mutex.set(true); } else { logger.warn("lock lost with scene:no less ,id[] and node[]", id, idName); unlock();// 可能自己的节点已超时挂了,所以id和ownerId不相同 } } } } } while (id == null); } catch (KeeperException e) { exception = e; if (mutex != null) { mutex.set(true); } } catch (InterruptedException e) { interrupt = e; if (mutex != null) { mutex.set(true); } } catch (Throwable e) { other = e; if (mutex != null) { mutex.set(true); } } if (isOwner() && mutex != null) { mutex.set(true); } return Boolean.FALSE; } private long getSessionId() { IZkConnection connection = zookeeper.getConnection(); return ((ZooKeeperx) connection).getZookeeper().getSessionId(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy