分布式锁实现方式一 基于zookeeper的分布式锁

本文介绍了一种基于Zookeeper实现的分布式锁方案,该方案能够有效解决分布式系统中多个节点同时访问共享资源产生的冲突问题,并详细阐述了其实现原理及关键代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分布式锁实现方式一 基于zookeeper的分布式锁


一。为何使用分布式锁?

当应用服务器数量超过1台,对相同数据的访问可能造成访问冲突(特别是写冲突)。单纯使用关系数据库比如MySQL的应用可以借助于事务来实现锁,也可以使用版本号等实现乐观锁,最大的缺陷就是可用性降低(性能差)。对于GLEASY这种满足大规模并发访问请求的应用来说,使用数据库事务来实现数据库就有些捉襟见肘了。另外对于一些不依赖数据库的应用,比如分布式文件系统,为了保证同一文件在大量读写操作情况下的正确性,必须引入分布式锁来约束对同一文件的并发操作。

二。对分布式锁的要求

1.高性能(分布式锁不能成为系统的性能瓶颈)
2.避免死锁(拿到锁的结点挂掉不会导致其它结点永远无法继续)
3.支持锁重入

三。方案1,基于zookeeper的分布式锁


/** 
 * DistributedLockUtil.java 
 * 分布式锁工厂类,所有分布式请求都由该工厂类负责 
 */  
public class DistributedLockUtil {  
    private static Object schemeLock = new Object();  
    private static Object mutexLock = new Object();  
    private static Map<String, Object> mutexLockMap = new ConcurrentHashMap();  
    private String schema;  
    private Map<String, DistributedReentrantLock> cache = new ConcurrentHashMap<String, DistributedReentrantLock>();  
  
    private static Map<String, DistributedLockUtil> instances = new ConcurrentHashMap();  
  
    public static DistributedLockUtil getInstance(String schema) {  
        DistributedLockUtil u = instances.get(schema);  
        if (u == null) {  
            synchronized (schemeLock) {  
                u = instances.get(schema);  
                if (u == null) {  
                    u = new DistributedLockUtil(schema);  
                    instances.put(schema, u);  
                }  
            }  
        }  
        return u;  
    }  
  
    private DistributedLockUtil(String schema) {  
        this.schema = schema;  
    }  
  
    private Object getMutex(String key) {  
        Object mx = mutexLockMap.get(key);  
        if (mx == null) {  
            synchronized (mutexLock) {  
                mx = mutexLockMap.get(key);  
                if (mx == null) {  
                    mx = new Object();  
                    mutexLockMap.put(key, mx);  
                }  
            }  
        }  
        return mx;  
    }  
  
    private DistributedReentrantLock getLock(String key) {  
        DistributedReentrantLock lock = cache.get(key);  
        if (lock == null) {  
            synchronized (getMutex(key)) {  
                lock = cache.get(key);  
                if (lock == null) {  
                    lock = new DistributedReentrantLock(key, schema);  
                    cache.put(key, lock);  
                }  
            }  
        }  
        return lock;  
    }  
  
    public void reset() {  
        for (String s : cache.keySet()) {  
            getLock(s).unlock();  
        }  
    }  
  
    /** 
     * 尝试加锁 
     * 如果当前线程已经拥有该锁的话,直接返回false,表示不用再次加锁,此时不应该再调用unlock进行解锁 
     * 
     * @param key 
     * @return 
     * @throws InterruptedException 
     * @throws KeeperException 
     */  
    public LockStat lock(String key) throws InterruptedException, KeeperException {  
        if (getLock(key).isOwner()) {  
            return LockStat.NONEED;  
        }  
        getLock(key).lock();  
        return LockStat.SUCCESS;  
    }  
  
    public void clearLock(String key) throws InterruptedException, KeeperException {  
        synchronized (getMutex(key)) {  
            DistributedReentrantLock l = cache.get(key);  
            l.clear();  
            cache.remove(key);  
        }  
    }  
  
    public void unlock(String key, LockStat stat) throws InterruptedException, KeeperException {  
        unlock(key, stat, false);  
    }  
  
    public void unlock(String key, LockStat stat, boolean keepalive) throws InterruptedException, KeeperException {  
        if (stat == null) return;  
        if (LockStat.SUCCESS.equals(stat)) {  
            DistributedReentrantLock lock = getLock(key);  
            boolean hasWaiter = lock.unlock();  
            if (!hasWaiter && !keepalive) {  
                synchronized (getMutex(key)) {  
                    lock.clear();  
                    cache.remove(key);  
                }  
            }  
        }  
    }  
  
    public static enum LockStat {  
        NONEED,  
        SUCCESS  
    }  
}  

/** 
 * DistributedReentrantLock.java 
 * 本地线程之间锁争用,先使用虚拟机内部锁机制,减少结点间通信开销 
 */  
public class DistributedReentrantLock {  
    private static final Logger logger = Logger.getLogger(DistributedReentrantLock.class);  
    private ReentrantLock reentrantLock = new ReentrantLock();  
  
    private WriteLock writeLock;  
    private long timeout = 3 * 60 * 1000;  
  
    private final Object mutex = new Object();  
    private String dir;  
    private String schema;  
  
    private final ExitListener exitListener = new ExitListener() {  
        @Override  
        public void execute() {  
            initWriteLock();  
        }  
    };  
  
    private synchronized void initWriteLock() {  
        logger.debug("初始化writeLock");  
        writeLock = new WriteLock(dir, new LockListener() {  
  
            @Override  
            public void lockAcquired() {  
                synchronized (mutex) {  
                    mutex.notify();  
                }  
            }  
  
            @Override  
            public void lockReleased() {  
            }  
  
        }, schema);  
  
        if (writeLock != null && writeLock.zk != null) {  
            writeLock.zk.addExitListener(exitListener);  
        }  
  
        synchronized (mutex) {  
            mutex.notify();  
        }  
    }  
  
    public DistributedReentrantLock(String dir, String schema) {  
        this.dir = dir;  
        this.schema = schema;  
        initWriteLock();  
    }  
  
    public void lock(long timeout) throws InterruptedException, KeeperException {  
        reentrantLock.lock();//多线程竞争时,先拿到第一层锁  
        try {  
            boolean res = writeLock.trylock();  
            if (!res) {  
                synchronized (mutex) {  
                    mutex.wait(timeout);  
                }  
                if (writeLock == null || !writeLock.isOwner()) {  
                    throw new InterruptedException("锁超时");  
                }  
            }  
        } catch (InterruptedException e) {  
            reentrantLock.unlock();  
            throw e;  
        } catch (KeeperException e) {  
            reentrantLock.unlock();  
            throw e;  
        }  
    }  
  
    public void lock() throws InterruptedException, KeeperException {  
        lock(timeout);  
    }  
  
    public void destroy() throws KeeperException {  
        writeLock.unlock();  
    }  
  
  
    public boolean unlock() {  
        if (!isOwner()) return false;  
        try {  
            writeLock.unlock();  
            reentrantLock.unlock();//多线程竞争时,释放最外层锁  
        } catch (RuntimeException e) {  
            reentrantLock.unlock();//多线程竞争时,释放最外层锁  
            throw e;  
        }  
  
        return reentrantLock.hasQueuedThreads();  
    }  
  
  
    public boolean isOwner() {  
        return reentrantLock.isHeldByCurrentThread() && writeLock.isOwner();  
    }  
  
    public void clear() {  
        writeLock.clear();  
    }  
  
}  

/** 
 * WriteLock.java 
 * 基于zk的锁实现 
 * 一个最简单的场景如下: 
 * 1.结点A请求加锁,在特定路径下注册自己(会话自增结点),得到一个ID号1 
 * 2.结点B请求加锁,在特定路径下注册自己(会话自增结点),得到一个ID号2 
 * 3.结点A获取所有结点ID,判断出来自己是最小结点号,于是获得锁 
 * 4.结点B获取所有结点ID,判断出来自己不是最小结点,于是监听小于自己的最大结点(结点A)变更事件 
 * 5.结点A拿到锁,处理业务,处理完,释放锁(删除自己) 
 * 6.结点B收到结点A变更事件,判断出来自己已经是最小结点号,于是获得锁。 
 */  
public class WriteLock extends ZkPrimative {  
    private static final Logger LOG = Logger.getLogger(WriteLock.class);  
  
    private final String dir;  
    private String id;  
    private LockNode idName;  
    private String ownerId;  
    private String lastChildId;  
    private byte[] data = {0x12, 0x34};  
    private LockListener callback;  
  
    public WriteLock(String dir, String schema) {  
        super(schema, true);  
        this.dir = dir;  
    }  
  
    public WriteLock(String dir, LockListener callback, String schema) {  
        this(dir, schema);  
        <a href = "http://www.nbso.ca/" > nbso online casino reviews</a > this.callback = callback;  
    }  
  
    public LockListener getLockListener() {  
        return this.callback;  
    }  
  
    public void setLockListener(LockListener callback) {  
        this.callback = callback;  
    }  
  
    public synchronized void unlock() throws RuntimeException {  
        if (zk == null || zk.isClosed()) {  
            return;  
        }  
        if (id != null) {  
            try {  
                zk.delete(id, -1);  
            } catch (InterruptedException e) {  
                LOG.warn("Caught: "e, e);  
                //set that we have been interrupted.  
                Thread.currentThread().interrupt();  
            } catch (KeeperException.NoNodeException e) {  
                // do nothing  
            } catch (KeeperException e) {  
                LOG.warn("Caught: "e, e);  
                throw (RuntimeException) new RuntimeException(e.getMessage()).  
                        initCause(e);  
            } finally {  
                if (callback != null) {  
                    callback.lockReleased();  
                }  
                id = null;  
            }  
        }  
    }  
  
    private class LockWatcher implements Watcher {  
        public void process(WatchedEvent event) {  
            LOG.debug("Watcher fired on path: "event.getPath()" state: "  
                    event.getState()" type "event.getType());  
            try {  
                trylock();  
            } catch (Exception e) {  
                LOG.warn("Failed to acquire lock: "e, e);  
            }  
        }  
    }  
  
    private void findPrefixInChildren(String prefix, ZooKeeper zookeeper, String dir)  
            throws KeeperException, InterruptedException {  
        List<String> names = zookeeper.getChildren(dir, false);  
        for (String name : names) {  
            if (name.startsWith(prefix)) {  
                id = dir "/" name;  
                if (LOG.isDebugEnabled()) {  
                    LOG.debug("Found id created last time: "id);  
                }  
                break;  
            }  
        }  
        if (id == null) {  
            id = zookeeper.create(dir"/"prefix, data,  
                    acl, EPHEMERAL_SEQUENTIAL);  
  
            if (LOG.isDebugEnabled()) {  
                LOG.debug("Created id: "id);  
            }  
        }  
  
    }  
  
    public void clear() {  
        if (zk == null || zk.isClosed()) {  
            return;  
        }  
        try {  
            zk.delete(dir, -1);  
        } catch (Exception e) {  
            LOG.error("clear error: "e, e);  
        }  
    }  
  
    public synchronized boolean trylock() throws KeeperException, InterruptedException {  
        if (zk == null) {  
            LOG.info("zk 是空");  
            return false;  
        }  
        if (zk.isClosed()) {  
            LOG.info("zk 已经关闭");  
            return false;  
        }  
        ensurePathExists(dir);  
  
        LOG.debug("id:"id);  
        do {  
            if (id == null) {  
                long sessionId = zk.getSessionId();  
                String prefix = "x-" sessionId "-";  
                idName = new LockNode(id);  
                LOG.debug("idName:"idName);  
            }  
            if (id != null) {  
                List<String> names = zk.getChildren(dir, false);  
                if (names.isEmpty()) {  
                    LOG.warn("No children in: "dir" when we've just "  
                            "created one! Lets recreate it...");  
                    id = null;  
                } else {  
                    SortedSet<LockNode> sortedNames = new TreeSet<LockNode>();  
                    for (String name : names) {  
                        sortedNames.add(new LockNode(dir"/"name));  
                    }  
                    ownerId = sortedNames.first().getName();  
                    LOG.debug("all:"sortedNames);  
                    SortedSet<LockNode> lessThanMe = sortedNames.headSet(idName);  
                    LOG.debug("less than me:"lessThanMe);  
                    if (!lessThanMe.isEmpty()) {  
                        LockNode lastChildName = lessThanMe.last();  
                        lastChildId = lastChildName.getName();  
                        if (LOG.isDebugEnabled()) {  
                            LOG.debug("watching less than me node: "lastChildId);  
                        }  
                        Stat stat = zk.exists(lastChildId, new LockWatcher());  
                        if (stat != null) {  
                            return Boolean.FALSE;  
                        } else {  
                            LOG.warn("Could not find the"  
                                    " stats for less than me: "lastChildName.getName());  
                        }  
                    } else {  
                        if (isOwner()) {  
                            if (callback != null) {  
                                callback.lockAcquired();  
                            }  
                            return Boolean.TRUE;  
                        }  
                    }  
                }  
            }  
        }  
        while (id == null);  
        return Boolean.FALSE;  
    }  
  
    public String getDir() {  
        return dir;  
    }  
  
    public boolean isOwner() {  
        return id != null && ownerId != null && id.equals(ownerId);  
    }  
  
    public String getId() {  
        return this.id;  
    }  
}  



使用本方案实现的分布式锁,可以很好地解决锁重入的问题,而且使用会话结点来避免死锁;性能方面,根据笔者自测结果,加锁解锁各一次算是一个操作,本方案实现的分布式锁,TPS大概为2000-3000,性能比较一般;


--- end ---


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值