基于zookeeper集群下的分布式锁

我们常说的锁是单进程多线程锁,在多线程并发编程中,用于线程之间的数据同步,保护共享资源的访问。而分布式锁,指在分布式环境下,保护跨进程、跨主机、跨网络的共享资源,实现互斥访问,保证一致性。

架构图

左侧是zookeeper集群,locker是数据节点,node_1到node_n代表一系列的顺序节点。

右侧client_1至client_n代表客户端,Service代表需要互斥访问的服务。

总实现思路,是在获取锁的时候在locker节点下创建顺序节点,在释放锁的时候,把自己创建的节点删除。

流程图

1、新建zookeeper-lock工程

Lock.class:

public class Lock {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private ZkClient zkClient = null;
    private int SESSIONTIMEOUT = 25000;
    private int CONNECTIONTIMEOUT = 25000;

    public Lock() {
    }

    public Lock(String ipAddress) {
        this.zkClient = new ZkClient(ipAddress, SESSIONTIMEOUT, CONNECTIONTIMEOUT, new SerializableSerializer());
    }

    public void start() {
        logger.info("Lock 服务启动,准备初始化...");
        init();
    }

    public void init() {
        logger.info("Lock 开始初始化...");
    }

    /**
     * 创建节点
     *
     * @param data
     */
    public String create(String data) {
        boolean exists = zkClient.exists("/lock");
        if (!exists) {
            zkClient.createPersistent("/lock");
        }
        String path = "/lock".concat("/node-");
        String nodePath = zkClient.createEphemeralSequential(path, data);
        logger.info("{}创建{}节点,节点数据是:{}", data, nodePath, data);
        try {
            Thread.sleep(10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return nodePath;
    }

    /**
     * 获取锁
     *
     * @param nodePath
     */
    public void lock(String nodePath) {
        List<String> list = zkClient.getChildren("/lock");
        //升序排序
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                o1 = StringUtils.substringAfter(o1, "-");
                o2 = StringUtils.substringAfter(o2, "-");
                int num1 = Integer.parseInt(o1);
                int num2 = Integer.parseInt(o2);
                if (num1 > num2) {
                    return 1;
                } else if (num1 < num2) {
                    return -1;
                } else {
                    return 0;
                }
            }
        });
        logger.info("{}:节点{}的子节点有:{}", nodePath, "/lock", list.toString());
        String firtNodePath = "/lock".concat("/").concat(list.get(0));
        if (nodePath.equals(firtNodePath)) {
            String data = zkClient.readData(nodePath);
            logger.info("{}获取了锁", data);
            try {
                Thread.sleep(10000);
                release(nodePath);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            String data = zkClient.readData(nodePath);
            logger.info("{}等待获取锁", data);
            //获取上一个节点的位置信息
            String nodePath1 = StringUtils.substringAfterLast(nodePath, "/");
            int temp = 0;
            for (String string : list) {
                if (!string.equals(nodePath1)) {
                    temp++;
                }
            }
            String previousNodePath = "/lock".concat("/").concat(list.get(temp - 1));
            zkClient.subscribeDataChanges(previousNodePath, new IZkDataListener() {
                @Override
                public void handleDataChange(String s, Object o) throws Exception {

                }

                @Override
                public void handleDataDeleted(String s) throws Exception {
                    logger.info("{}节点释放了锁", s);
                    zkClient.unsubscribeDataChanges(s, new IZkDataListener() {
                        @Override
                        public void handleDataChange(String s, Object o) throws Exception {

                        }

                        @Override
                        public void handleDataDeleted(String s) throws Exception {

                        }
                    });
                    lock(nodePath);
                }
            });
        }
    }

    /**
     * 释放锁
     *
     * @param nodePath
     */
    public void release(String nodePath) {
        logger.info("{}释放锁", nodePath);
        zkClient.delete(nodePath);
    }
}

WebListener.class:

@Component
public class WebListener implements ServletContextListener {

    private Logger logger = LoggerFactory.getLogger(getClass());
    private String ipAddress = "192.168.202.128:2181,192.168.202.129:2181,192.168.202.130:2181";


    @Override
    public void contextInitialized(ServletContextEvent sce) {
        Lock lock = new Lock("192.168.202.128:2181");
        lock.start();
        String client1 = "client1";
        String client2 = "client2";
        String client3 = "client3";
        String nodePath1 = lock.create(client1);
        String nodePath2 = lock.create(client2);
        String nodePath3 = lock.create(client3);
        lock.lock(nodePath1);
        lock.lock(nodePath2);
        lock.lock(nodePath3);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

2、运行及运行结果:

猜你喜欢

转载自blog.csdn.net/LiaoHongHB/article/details/84982238