Zookeeper实现分布式锁代码实例

 更新时间:2023年12月07日 09:47:01   作者:外星喵  
这篇文章主要介绍了Zookeeper实现分布式锁代码实例,Zookeeper 分布式锁应用了其 临时顺序节点 的特性,在Zookeeper中创建一个持久节点ParentLock,当第一个客户端要获取锁时,在ParentLock节点下创建一个临时顺序节点,需要的朋友可以参考下

Zookeeper分布式锁原理

在这里插入图片描述

临时顺序节点

该类型的节点创建完成之后,若客户端与服务端断开连接之前没有执行delete操作,Zookeeper会自动删除该类型的节点,同时该类型的节点创建之后,Zookeeper会为该类型的节点在节点名称的基础上增加一个单调递增的编号;

Zookeeper 分布式锁应用了其 临时顺序节点 的特性。实现步骤如下:

获取锁

首先在Zookeeper中创建一个持久节点ParentLock,当第一个客户端要获取锁时,在ParentLock节点下创建一个临时顺序节点Lock1;

接下来客户端1会获取ParentLock下的所有临时顺序子节点并进行排序,然后与自身创建的Lock1比较,判断Lock1是不是最小的(最靠前的),如果是最靠前的,则获取锁成功;

此时,假设又有一个客户端2前来获取锁,则在ParentLock下创建一个临时顺序节点Lock2;

接下来客户端2会获取ParentLock下的所有临时顺序子节点并进行排序,然后与自身创建的Lock2比较,判断Lock2是不是最小的(最靠前的),结果发现Lock2不是最小的;

于是,Lock2向排序比它靠前的第一个节点Lock1注册一个Watcher,用于监听Lock1是否存在,此时意味着客户端2抢锁失败,客户端2进入等待状态;

此时,假设现在又有一个客户端3前来获取锁,则在ParentLock下创建一个临时顺序节点Lock3;

接下来客户端3将获取ParentLock下的所有临时顺序节点并排序,与自身创建的节点Lock3比较,判断Lock3是不是最小的(最靠前的),结果发现Lock3不是最小的;

于是,Lock3向比它靠前的第一个节点Lock2注册一个Watcher,用于监听Lock2是否存在,此时意味着Lock3也抢锁失败,进入阻塞状态; 这样的话,客户端1获取到了锁,客户端2监听了Lock1、客户端3监听了Lock2;

客户端崩溃释放锁(避免死锁)

假设由于网络原因或者其他物理原因,导致客户端1与Zookeeper失去连接,根据临时节点的特性,Zookeeper会自动删除相关联的节点Lock1。 Lock1删除之后,因为客户端2在Lock1上注册了Watcher,因此Lock1删除之后,客户端2会立即收到通知,这时客户端2会再次获取ParentLock下的所有临时顺序子节点并进行排序,然后与自身创建的Lock2比较,判断Lock2是不是最小的(最靠前的),结果发现Lock2是最小的,因此客户端2获取锁成功;

客户端2崩溃同理;

显示释放(客户端任务执行完毕,主动释放锁)

当客户端2执行完任务之后,调用delete方法删除Lock2节点,因为客户端3在Lock2上注册了Watcher,因此Lock2删除之后,客户端3会立即收到通知,这时客户端3会再次获取ParentLock下的所有临时顺序子节点并进行排序,然后与自身创建的Lock3比较,判断Lock3是不是最小的(最靠前的),结果发现Lock3是最小的,因此客户端3获取锁成功;

zookeeper分布式锁实现

配置文件pom.xml

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipesss</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.1</version>
        </dependency>

配置文件application.properties

##ZooKeeper 集成 Curator
zk.url = 127.0.0.1:2181

conf

@Configuration
@Slf4j
public class ZooKeeperConf {
    @Value("${zk.url}")
    private String zkUrl;
    @Bean
    public CuratorFramework getCuratorFramework() {
        // 用于重连策略,1000毫秒是初始化的间隔时间,3代表尝试重连次数
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient(zkUrl, retryPolicy);
        //必须调用start开始连接ZooKeeper
        client.start();
//        /**
//         * 使用Curator,可以通过LeaderSelector来实现领导选取;
//         * 领导选取:选出一个领导节点来负责其他节点;如果领导节点不可用,则在剩下的机器里再选出一个领导节点
//         */
//        // 构造一个监听器
//        LeaderSelectorListenerAdapter listener = new LeaderSelectorListenerAdapter() {
//            @Override
//            public void takeLeadership(CuratorFramework curatorFramework) throws Exception {
//                log.info("get leadership");
//                // 领导节点,方法结束后退出领导。zk会再次重新选择领导
//
//            }
//        };
//        LeaderSelector selector = new LeaderSelector(client, "/schedule", listener);
//        selector.autoRequeue();
//        selector.start();
        return client;
    }
}

controller

@RestController
@RequestMapping("/zookeeper")
public class ZooKeeperController {
    @Autowired
    private CuratorFramework zkClient;
    @Autowired
    private ZooKeeperImpl zooKeeper;
    /**
     * zookeeper 获取节点下的数据
     * <p>
     * post请求: http://localhost:8082/zookeeper/makeOrder?path=/task
     *
     * @param path
     * @return
     */
    @PostMapping("/getData")
    public String getData(@RequestParam String path) {
        byte[] bytes = null;
        try {
            bytes = zkClient.getData().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String str = new String(bytes);
        return str;
    }
    @PostMapping("/create")
    public String create(@RequestParam String path) {
        try {
            zkClient.create().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }
    @PostMapping("/delete")
    public String delete(@RequestParam String path) {
        try {
            zkClient.delete().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }
    @PostMapping("/setData")
    public String setData(@RequestParam(value = "path") String path, @RequestParam(value = "data") String data) {
        try {
            zkClient.setData().forPath(path, data.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }
    @PostMapping("/check")
    public String check(@RequestParam(value = "path") String path) {
        Stat stat = null;
        try {
            stat = zkClient.checkExists().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "stat" + stat;
    }
    @PostMapping("/children")
    public String children(@RequestParam(value = "path") String path) {
        List<String> children = null;
        try {
            children = zkClient.getChildren().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "children" + children;
    }
    @PostMapping("/watch")
    public String watch(@RequestParam(value = "path") String path) {
        Stat stat = null;
        try {
            stat = zkClient.checkExists().watched().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "watch " + stat;
    }
    /**
     * zookeeper分布式锁
     *
     * @param product
     * @return
     */
    @PostMapping("/makeOrder")
    public String makeOrder(@RequestParam(value = "product") String product) {
        zooKeeper.makeOrder(product);
        return "success";
    }
}

service

@Service
@Slf4j
public class ZooKeeperImpl {
    private static final String lockPath = "/lock/order";
    @Autowired
    private CuratorFramework zkClient;
    public void makeOrder(String product) {
        log.info("try do job for " + product);
        String path = lockPath + "/" + product;
        try {
        	// InterProcessMutex 构建一个分布式锁
            InterProcessMutex lock = new InterProcessMutex(zkClient, path);
            try {
                if (lock.acquire(5, TimeUnit.HOURS)) {
                	// 模拟业务处理耗时5秒
                    Thread.sleep(5*1000);
                    log.info("do job " + product + "done");
                }
            } finally {
            	// 释放该锁
                lock.release();
            }
        } catch (Exception e) {
        	// zk异常
            e.printStackTrace();
        }
    }
}

到此这篇关于Zookeeper实现分布式锁代码实例的文章就介绍到这了,更多相关Zookeeper分布式锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • J2SE基础之JDK环境变量配置

    J2SE基础之JDK环境变量配置

    进行java开发,首先要安装jdk,安装了jdk后还要进行环境变量配置,下面我们就来详细探讨下这个问题。
    2016-05-05
  • Spring配置多个数据源并实现数据源的动态切换功能

    Spring配置多个数据源并实现数据源的动态切换功能

    这篇文章主要介绍了Spring配置多个数据源并实现数据源的动态切换功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-01-01
  • java实现简易局域网聊天功能

    java实现简易局域网聊天功能

    这篇文章主要为大家详细介绍了java实现简易局域网聊天功能,使用UDP模式编写一个聊天程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • java编译时指定classpath的实现方法

    java编译时指定classpath的实现方法

    在Java编程中,classpath是用于指定Java虚拟机在运行时查找类文件的路径,本文主要介绍了java编译时指定classpath的实现方法,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • 浅谈@Aspect@Order各个通知的执行顺序

    浅谈@Aspect@Order各个通知的执行顺序

    这篇文章主要介绍了@Aspect@Order各个通知的执行顺序,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • SpringBoot整合mybatis使用Druid做连接池的方式

    SpringBoot整合mybatis使用Druid做连接池的方式

    这篇文章主要介绍了SpringBoot整合mybatis使用Druid做连接池的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • SpringBoot实现Tomcat集群的会话管理功能

    SpringBoot实现Tomcat集群的会话管理功能

    在使用 Tomcat 集群时,由于每个 Tomcat 实例的 Session 存储是独立的,导致无法实现 Session 的共享,这可能影响到用户跨节点的访问,为了实现跨 Tomcat 实例共享 Session,可以使用 Spring Session 配合 Redis 进行集中式会话管理,需要的朋友可以参考下
    2024-12-12
  • Mybatis自定义SQL的关系映射、分页、排序功能的实现

    Mybatis自定义SQL的关系映射、分页、排序功能的实现

    这篇文章主要介绍了Mybatis自定义SQL的关系映射、分页、排序功能的实现,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • 基于java URL和URLConnection(详解)

    基于java URL和URLConnection(详解)

    下面小编就为大家分享一篇基于java URL和URLConnection(详解),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • java高级用法之JNA中使用类型映射

    java高级用法之JNA中使用类型映射

    JNA中有很多种映射,本文主要介绍了java高级用法之JNA中使用类型映射,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-03-03

最新评论