基于Zookeeper实现分布式锁

 一、Zookeeper相关概念

基于Zookeeper实现分布式锁,主要依赖于它的【瞬时有序节点】,当多个客户端并发创建瞬时有序节点时,会自动为我们生成有序的节点,例如定义子节点名为 order_ ,则生成的节点为 order_00000001,下一个节点为 order_00000002,为我们保证有序。

利用Zookeeper节点的递增性,可以规定节点编号最小的那个获得锁。当编号最小的那个释放锁后,通知第二个节点获得锁(节点监听机制),以此类推,首尾相连。所以说Zookeeper天然支持分布式锁。

Zookeeper的内部机制,能保证后面的节点能够正常的监听到删除事件通知。若当前的客户端与Zookeeper集群服务器失去连接时,这个临时节点也将自动删除。排在它后面的那个节点,也能收到删除事件,正常执行。

 

二、Zookeeper实现分布式锁的步骤

  1. 创建持久化根节点,所有获取锁创建的瞬时有序节点都在该节点下
  2. 在根节点下创建瞬时有序节点
  3. 判断当前节点是否为第一个节点,是则获取到锁
  4. 若不是第一个节点,则遍历所有子节点,查找到上一个节点并创建监听
  5. 调用 wait();使得当前线程等待
  6. 当上一个节点删除后,通知该节点唤醒线程获取到锁
  7. 方法结束后释放锁,即删除瞬时节点

三、代码实现

package com.example.distributezklock.lock;

import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

@Slf4j
public class ZkLock implements AutoCloseable, Watcher {

    /**
     *  Zookeeper连接
     */
    private ZooKeeper zooKeeper;
    /**
     *  当前节点
     */
    private String znode;

    public ZkLock() throws IOException {
        // 创建Zookeeper连接
        this.zooKeeper = new ZooKeeper("localhost:2181",
                10000,this);
    }

    /**
     *  获取锁
     * @param lockCode 锁 code
     * @return 加锁结果
     */
    public boolean getLock(String lockCode) {
        try {
            // 创建业务 根节点
            Stat stat = zooKeeper.exists("/" + lockCode, false);
            if (null == stat){
                // 根节点不存在则创建,创建开放的,持久化节点
                zooKeeper.create("/" + lockCode,lockCode.getBytes(),
                        ZooDefs.Ids.OPEN_ACL_UNSAFE,
                        CreateMode.PERSISTENT);
            }

            // 创建瞬时有序节点  多个线程同时创建,则为生成有序的节点名 /order/order_00000001
            znode = zooKeeper.create("/" + lockCode + "/" + lockCode + "_", lockCode.getBytes(),
                    ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL_SEQUENTIAL);

            // 获取业务节点下 所有的子节点
            List<String> childrenNodes = zooKeeper.getChildren("/" + lockCode, false);
            // 子节点排序
            Collections.sort(childrenNodes);
            // 获取序号最小的(第一个)子节点
            String firstNode = childrenNodes.get(0);
            // 如果创建的节点是第一个子节点,则获得锁
            if (znode.endsWith(firstNode)){
                return true;
            }
            // 不是第一个子节点,则监听前一个节点
            String lastNode = firstNode;
            for (String node:childrenNodes){
                if (znode.endsWith(node)){
                    // 监听前一个节点
                    zooKeeper.exists("/"+lockCode+"/"+lastNode,true);
                    break;
                }else {
                    // 前节点
                    lastNode = node;
                }
            }
            // 执行等待,等待唤醒,固定写法
            synchronized (this){
                wait();
            }

            return true;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     *  自动关闭方法,实现AutoCloseable接口
     * @throws Exception
     */
    @Override
    public void close() throws Exception {
        // 删除当前瞬时节点,释放锁
        zooKeeper.delete(znode,-1);
        // 关闭Zookeeper连接
        zooKeeper.close();
    }

    /**
     *  通知方法,实现Watcher接口
     * @param event 事件
     */
    @Override
    public void process(WatchedEvent event) {
        // 监听到前一个节点被删除,则 [唤醒] 当前线程
        if (event.getType() == Event.EventType.NodeDeleted){
            synchronized (this){
                notify();
            }
        }
    }
}

四、ZooKeeper客户端框架curator

比较完善的ZooKeeper客户端框架,通过封装的一套高级API 简化了ZooKeeper的操作。通过查看官方文档,可以发现Curator主要解决了三类问题:

  • 封装ZooKeeper client与ZooKeeper server之间的连接处理
  • 提供了一套Fluent风格的操作API
  • 提供ZooKeeper各种应用场景(recipe, 比如:分布式锁服务、集群领导选举、共享计数器、缓存机制、分布式队列等)的抽象封装

建议生产环境中使用该框架进行实现Zookeeper分布式锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值