Zookeeper manually implements distributed locks and the Curator framework

Implementation principle:

The client accesses the cluster. After receiving the request, it creates a temporary sequence node. The smaller the sequence number is, the higher the priority is. It determines whether it is the smallest node or not, and monitors the previous node.

First establish a connection, if there is no connection, wait for a delay, determine whether the root node exists, and there is no creation

Lock, create nodes, determine the number of nodes, if not one, sort and compare, and monitor the previous node

When waiting for a node to be deleted from the monitor, and the deletion is his previous node, release the lock

Manual implementation, write code

package com.lzq.zk2;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class DistributedLock {
    // zookeeper server 列表
    private String connectString =
            "192.168.6.100:2181,192.168.6.101:2181,192.168.6.102:2181";
    // 超时时间
    private 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;
    // 和 zk 服务建立连接,并创建根节点
    public DistributedLock() throws IOException,
            InterruptedException, KeeperException {
        zk = new ZooKeeper(connectString, sessionTimeout, new
                Watcher() {
                    @Override
                    public void process(WatchedEvent event) {
                        // 连接建立时, 打开 latch, 唤醒 wait 在该 latch 上的线程
                        if (event.getState() ==
                                Event.KeeperState.SyncConnected) {
                            connectLatch.countDown();
                        }
                        // 发生了 waitPath 的删除事件
                        if (event.getType() ==
                                Event.EventType.NodeDeleted && event.getPath().equals(waitPath))
                        {
                            waitLatch.countDown();
                        }
                    }
                });
        // 等待连接建立
        connectLatch.await();
        //获取根节点状态
        Stat stat = zk.exists("/" + rootNode, false);
        //如果根节点不存在,则创建根节点,根节点类型为永久节点
        if (stat == null) {
            System.out.println("根节点不存在");
            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);
            // wait 一小会, 让结果更清晰一些
            Thread.sleep(10);
            // 注意, 没有必要监听"/locks"的子节点的变化情况
            List<String> childrenNodes = zk.getChildren("/" +
                    rootNode, false);
            // 列表中只有一个子节点, 那肯定就是 currentNode , 说明client 获得锁
            if (childrenNodes.size() == 1) {
                return;
            } else {
                //对根节点下的所有临时顺序节点进行从小到大排序
                Collections.sort(childrenNodes);
                //当前节点名称
                String thisNode = currentNode.substring(("/" +
                        rootNode + "/").length());
                //获取当前节点的位置
                int index = childrenNodes.indexOf(thisNode);
                if (index == -1) {
                    System.out.println("数据异常");
                } else if (index == 0) {
                    // index == 0, 说明 thisNode 在列表中最小, 当前client 获得锁
                    return;
                } else {
                    // 获得排名比 currentNode 前 1 位的节点
                    this.waitPath = "/" + rootNode + "/" +
                            childrenNodes.get(index - 1);
                    // 在 waitPath 上注册监听器, 当 waitPath 被删除时,zookeeper 会回调监听器的 process 方法
                    zk.getData(waitPath, true, new Stat());
                    //进入等待锁状态
                    waitLatch.await();
                    return;
                }
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    // 解锁方法
    public void zkUnlock() {
        try {
            zk.delete(this.currentNode, -1);
        } catch (InterruptedException | KeeperException e) {
            e.printStackTrace();
        }
    }
}

Test creates two threads

import org.apache.zookeeper.KeeperException;
import java.io.IOException;
public class DistributedLockTest {
    public static void main(String[] args) throws InterruptedException, IOException, KeeperException {
        // 创建分布式锁 1
        final DistributedLock lock1 = new DistributedLock();
        // 创建分布式锁 2
        final DistributedLock lock2 = new DistributedLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 获取锁对象
                try {
                    lock1.zkLock();
                    System.out.println("线程 1 获取锁");
                    Thread.sleep(5 * 1000);
                    lock1.zkUnlock();
                    System.out.println("线程 1 释放锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 获取锁对象
                try {
                    lock2.zkLock();
                    System.out.println("线程 2 获取锁");
                    Thread.sleep(5 * 1000);
                    lock2.zkUnlock();
                    System.out.println("线程 2 释放锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

Curator framework implementation

add dependencies

<dependency>
 <groupId>org.apache.curator</groupId>
 <artifactId>curator-framework</artifactId>
 <version>4.3.0</version>
</dependency>
<dependency>
 <groupId>org.apache.curator</groupId>
 <artifactId>curator-recipes</artifactId>
 <version>4.3.0</version>
</dependency>
<dependency>
 <groupId>org.apache.curator</groupId>
 <artifactId>curator-client</artifactId>
 <version>4.3.0</version>
</dependency>

Create test class

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;
public class CuratorLockTest {
 	private String rootNode = "/locks";
    // zookeeper server 列表
     private String connectString = 
    "192.168.6.100:2181,192.168.6.101:2181,192.168.6.102:2181";
     // connection 超时时间
     private int connectionTimeout = 2000;
     // session 超时时间
     private int sessionTimeout = 2000;
     public static void main(String[] args) {
     new CuratorLockTest().test();
     }
 // 测试
 private void test() {
         // 创建分布式锁 1
         final InterProcessLock lock1 = new 
       	 InterProcessMutex(getCuratorFramework(), rootNode);
         // 创建分布式锁 2
         final InterProcessLock lock2 = new InterProcessMutex(getCuratorFramework(), rootNode);
         new Thread(new Runnable() {
         @Override
         public void run() {
         // 获取锁对象
         try {
         lock1.acquire();
         System.out.println("线程 1 获取锁");
         // 测试锁重入
        lock1.acquire();
         System.out.println("线程 1 再次获取锁");
         Thread.sleep(5 * 1000);
        lock1.release();
         System.out.println("线程 1 释放锁");
         lock1.release();
         System.out.println("线程 1 再次释放锁");
         } catch (Exception e) {
         e.printStackTrace();
         }
 	}
 }).start();
     new Thread(new Runnable() {
     @Override
     public void run() {
         // 获取锁对象
             try {
             lock2.acquire();
             System.out.println("线程 2 获取锁");
             // 测试锁重入
            lock2.acquire();
            System.out.println("线程 2 再次获取锁");
             Thread.sleep(5 * 1000);
            lock2.release();
             System.out.println("线程 2 释放锁");
             lock2.release();
             System.out.println("线程 2 再次释放锁");
             } catch (Exception e) {
             e.printStackTrace();
             }
         }
     }).start();
 }
 // 分布式锁初始化
 public CuratorFramework getCuratorFramework (){
     //重试策略,初试时间 3 秒,重试 3 次
         RetryPolicy policy = new ExponentialBackoffRetry(3000, 3);
         //通过工厂创建 Curator
         CuratorFramework client = CuratorFrameworkFactory.builder()
         .connectString(connectString)
         .connectionTimeoutMs(connectionTimeout)
         .sessionTimeoutMs(sessionTimeout)
         .retryPolicy(policy).build();
         //开启连接
         client.start();
         System.out.println("zookeeper 初始化完成...");
         return client;
     }
}

Guess you like

Origin blog.csdn.net/weixin_52210557/article/details/123437503