zookeeper分布式锁原理及项目应用

https://www.cnblogs.com/linjiqin/p/6052031.html

 

pom.xml

        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.11</version>
        </dependency>

  

logback.xml

<?xml version="1.0"?>
<configuration>

    <!-- ch.qos.logback.core.ConsoleAppender 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder charset="UTF-8">
            <pattern>[%-5level] %d{HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 日志级别 -->
    <root>
        <level value="info" />
        <appender-ref ref="console" />
    </root>

</configuration>

  GenerateOrderNoService

public class GenerateOrderNoService {

    static int count = 0;
    public static int getOrderNo() {
        count++;
        return count;
    }
}

  main

public class OrderService implements Runnable {
    ZookeeperLock lock = new ZookeeperLock();

    @Override
    public void run() {
        int orderNo = getOrderNo();
        System.out.println(Thread.currentThread().getName() + " 测试 " + " : " + orderNo);
    }

    private int getOrderNo() {
        try {
            lock.lock();
            int orderNo = 0;
            for (int i = 1; i <= 1000; i++) {
                orderNo = GenerateOrderNoService.getOrderNo();
            }
            return orderNo;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return 0;
    }

    public static void main(String[] args) throws InterruptedException {
        OrderService orderService = new OrderService();
        for (int i = 1; i <= 5; i++) {
            Thread thread = new Thread(orderService);
            thread.start();
//            Thread.sleep(200);
        }

    }
}

  

ZookeeperLock

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;

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

/**
 * 持久化根节点: /lock
 * 最小节点才能获取锁
 * 非最小节点watch监听上一个节点,监听是否删除操作,删除才获取锁,否则阻塞等待
 * 获取锁,执行完毕业务,需要释放锁,释放锁即删除临时节点
 */
public class ZookeeperLock {
    private String host = "192.168.239.131:2181,192.168.239.132:2181,192.168.239.133:2181,192.168.239.104:134";
    private ZkClient zkClient;
    private String persistentNodePath = "/lock";

    private ThreadLocal<String> curTl = new ThreadLocal<>();         //当前节点  /lock/t0001
    private ThreadLocal<String> beforeTl = new ThreadLocal<>();      //前个节点  /lock/t0002  lastZoneId - zoneId = 1


    public ZookeeperLock() {
        zkClient = new ZkClient(host);

        if (!zkClient.exists(persistentNodePath)) {
            zkClient.createPersistent(persistentNodePath,"");
        }
    }

    /**
     * 尝试获取锁
     *
     * 最小节点   : 得到锁。
     * 非最小节点 : 等待锁。
     *
     * @return true:得到锁, false:未得到锁
     */
    private boolean tryLock() {
        //创建当前临时节点
        String currentEphemeralZoneId = curTl.get();
        if(currentEphemeralZoneId == null) {
            currentEphemeralZoneId = zkClient.createEphemeralSequential(persistentNodePath + "/", null);
            System.out.println(Thread.currentThread() + "  创建临时节点 " + currentEphemeralZoneId);
            curTl.set(currentEphemeralZoneId);
        }

        //获取/lock下所有子节点
        List<String> children = zkClient.getChildren(persistentNodePath);

        //排序
        Collections.sort(children);

        //获取当前节点
        String first = children.stream().findFirst().get();

        //是否为最小节点
        if (currentEphemeralZoneId.equals(persistentNodePath + "/" + first)) {
            return true;
        } else {
            //获取上一个节点
            int curIndex = children.indexOf(currentEphemeralZoneId.substring(persistentNodePath.length() + 1));
            String beforeEphemeralZoneId = persistentNodePath + "/" + children.get(curIndex - 1);
            beforeTl.set(beforeEphemeralZoneId);
            System.out.println(Thread.currentThread() + " 当前:" + currentEphemeralZoneId + ",  前个:" + beforeEphemeralZoneId);
            return false;
        }
    }

    /**
     * 等待锁 : 未获取到锁 则阻塞并等待锁
     * 注意:要先注册监听器,
     * 如果后注册监听器,会导致监听器未启动,线程便进入await,await等不到countDownLatch.countDown();就会一直处于等待状态,陷入死锁。
     *
     * @return
     */
    private void waitForLock() throws InterruptedException {
        String beforeEphemeralZoneId = beforeTl.get();
        String currentEphemeralZoneId = curTl.get();

        //线程计数器
        CountDownLatch countDownLatch = new CountDownLatch(1);

        //构造监听器
        IZkDataListener listener = new IZkDataListener() {
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {
                System.out.println("节点改变");
            }

            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                System.out.println(Thread.currentThread() + " 监听到" + beforeEphemeralZoneId  + "被删除, 唤醒" + currentEphemeralZoneId);
                //删除上个节点, 唤醒当前线程
                countDownLatch.countDown();
            }
        };



        //开启监听器
        zkClient.subscribeDataChanges(beforeEphemeralZoneId, listener);

        //二次确认:是否存在前个节点
        if(zkClient.exists(beforeEphemeralZoneId)) {
            //存在:阻塞当前线程
            System.out.println(Thread.currentThread() + " " + currentEphemeralZoneId +" 进等待了! ");
            countDownLatch.await();
            System.out.println(currentEphemeralZoneId +" 拿到锁了!");
        } else {
            //不存在:关闭监听器
            zkClient.unsubscribeDataChanges(beforeEphemeralZoneId, listener);
        }
    }

    /**
     * 获取锁
     * @return
     */
    public void lock() throws Exception {
        if(!tryLock()) {
            waitForLock();
            lock();
        }
    }

    /**
     * 释放锁
     * @return
     */
    public void unlock() {
        String currentEphemeralZoneId = curTl.get();
        System.out.println("释放锁,删除节点 " + currentEphemeralZoneId);
        zkClient.delete(currentEphemeralZoneId);
        curTl.remove();
    }
}

  

 

posted @ 2019-01-02 00:47  Peter.Jones  阅读(96)  评论(0编辑  收藏  举报