【Zookeeper学习笔记】分布式锁实现

【Zookeeper学习笔记】分布式锁实现


文章目录

  • 【Zookeeper学习笔记】分布式锁实现
  • 前言
  • 一、Zookeeper是什么?
  • 二、Java API模式
    • 1.引入依赖
    • 2. log4j.properties配置
    • 3. 代码实现
  • 三、curator模式
    • 1. 添加curator依赖
    • 2. 业务代码
  • 总结


前言

Zookeeper是一个分布式管理框架,本文仅展示分布式锁代码


一、Zookeeper是什么?

Zookeeper 是一个开源的分布式的,为分布式框架提供协调服务的 Apache 项目。是一个基
于观察者模式设计的分布式服务管理框架。

二、Java API模式

1.引入依赖

代码如下(示例):

		
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-coreartifactId>
            <version>2.8.2version>
        dependency>
		
        <dependency>
            <groupId>org.apache.zookeepergroupId>
            <artifactId>zookeeperartifactId>
            <version>3.5.7version>
        dependency>

2. log4j.properties配置

代码如下(示例):

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

3. 代码实现

代码如下(示例):

package com.demo.zk.lock;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author 小肆2019
 * @date 2023/3/25/025
 * @desc to do something
 * @since 1.0
 */
public class ZkLock {

    private final String connectString = "110.40.194.181:2181";

    private final int sessionTimeout = 2000;

    private ZooKeeper zk;

    private String rootNode = "locks";

    private String subNode = "seq-";
    // 当前 client 等待的子节点
    private String waitPath;
    //ZooKeeper 连接
    private CountDownLatch connectLatch = new CountDownLatch(1);
    //ZooKeeper 节点等待
    private CountDownLatch waitLatch = new CountDownLatch(1);

    // 当前 client 创建的子节点
    private String currentNode;

    public ZkLock() throws Exception {
        zk = new ZooKeeper(connectString, sessionTimeout, watchedEvent -> {
            if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
                connectLatch.countDown();
            }
            if (watchedEvent.getType() == Watcher.Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)) {
                waitLatch.countDown();
            }
        });
        // 等待连接建立
        connectLatch.await();
        // 获取根节点状态
        Stat stat = zk.exists("/" + rootNode, false);
        // 如果根节点不存在,则创建根节点,根节点类型为永久节点
        if (stat == null) {
            System.out.println("rootNode not exist");
            zk.create("/" + rootNode, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }

    // 加锁
    public void zkLock() {
        try {
            currentNode  = zk.create(
                    "/" + rootNode + "/" + subNode,
                    null,
                    ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL_SEQUENTIAL);
            Thread.sleep(10);
            // 注意, 没有必要监听"/locks"的子节点的变化情况
            List<String> children = zk.getChildren("/" + rootNode, false);
            // 列表中只有一个子节点,那肯定就是currentNode。说明client获取锁
            if (children.size() == 1) {
                return;
            } else {
                // 对根节点下的所有临时顺序节点进行从小到大排序
                Collections.sort(children);
                String thisNode = currentNode.substring(("/" + rootNode + "/").length());
                // 获取当前节点的位置
                int index = children.indexOf(thisNode);
                if (index == -1) {
                    System.out.println("data error");
                } else if (index == 0) {
                    // index == 0, 说明 thisNode 在列表中最小, 当前client 获得锁
                    return;
                } else {
                    // 获得排名比 currentNode 前1位的节点
                    this.waitPath = "/" + rootNode + "/" + children.get(index - 1);
                    // 在waitPath上注册监听器,当waitPath删除时,zookeeper会回调监听器的方法
                    zk.getData(waitPath,true,new Stat());
                    // 进入等待锁状态
                    waitLatch.await();
                    return;
                }
            }
        } catch (KeeperException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    // 解锁
    public void zkUnlock(){
        try {
            zk.delete(this.currentNode,-1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (KeeperException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        ZkLock lock1 = new ZkLock();
        ZkLock lock2 = new ZkLock();

        new Thread(() -> {
            lock1.zkLock();
            System.out.println("线程 1 获取锁");
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            lock1.zkUnlock();
            System.out.println("线程 1 释放锁");
        }).start();
        new Thread(() -> {
            lock2.zkLock();
            System.out.println("线程 2 获取锁");
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            lock2.zkUnlock();
            System.out.println("线程 2 释放锁");
        }).start();
    }

}

说明:java代码实现方式是利用的CountDownLatch锁的特性,当CountDownLatch锁的state值不为0就会一直进行阻塞。当我本次创建的节点按照从小到大的顺序排列不是第一个节点,就说明没有抢到锁,于是监听上一个节点,等到上一个节点被删除(释放锁),再获取锁。

三、curator模式

1. 添加curator依赖

代码如下(示例):

        <dependency>
            <groupId>org.apache.curatorgroupId>
            <artifactId>curator-frameworkartifactId>
            <version>4.3.0version>
        dependency>
        <dependency>
            <groupId>org.apache.curatorgroupId>
            <artifactId>curator-recipesartifactId>
            <version>4.3.0version>
        dependency>
        <dependency>
            <groupId>org.apache.curatorgroupId>
            <artifactId>curator-clientartifactId>
            <version>4.3.0version>
        dependency>

2. 业务代码

代码如下(示例):

package com.demo.zk.curator;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

/**
 * @author wo595
 * @date 2023/3/25/025
 * @desc to do something
 * @since 1.0
 */
public class CuratorLock {

    private String rootNode = "/locks";

    private String connectString = "110.40.194.181:2181";

    private int sessionTimeout = 20000;

    private int connectionTimeout = 20000;

    public static void main(String[] args) {
        new CuratorLock().test();
    }

    public void test() {
        final InterProcessLock lock1 = new InterProcessMutex(getCuratorFramework(), rootNode);
        final InterProcessLock lock2 = new InterProcessMutex(getCuratorFramework(), rootNode);

        new Thread(() -> {
            try {
                // 获取锁对象
                lock1.acquire();
                System.out.println("thread1 acquire lock");
                // 测试锁的重入
                lock1.acquire();
                System.out.println("thread1 acquire lock again");
                Thread.sleep(5000);
                lock1.release();
                System.out.println("thread1 release lock");
                lock1.release();
                System.out.println("thread1 release lock again");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                // 获取锁对象
                lock2.acquire();
                System.out.println("thread2 acquire lock");
                // 测试锁的重入
                lock2.acquire();
                System.out.println("thread2 acquire lock again");
                Thread.sleep(5000);
                lock2.release();
                System.out.println("thread2 release lock");
                lock2.release();
                System.out.println("thread2 release lock again");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    private CuratorFramework getCuratorFramework() {
        RetryPolicy policy = new ExponentialBackoffRetry(3000, 3);
        CuratorFramework build = CuratorFrameworkFactory.builder()
                .connectString(connectString)
                .connectionTimeoutMs(connectionTimeout)
                .sessionTimeoutMs(sessionTimeout)
                .retryPolicy(policy)
                .build();
        build.start();
        System.out.println("init zookeeper success");
        return build;
    }
}


总结

使用curator。

你可能感兴趣的:(Zookeeper,分布式,java-zookeeper,zookeeper)