基于Curator操作ZooKeeper(四)-自定义实现简易分布式锁

版权声明: https://blog.csdn.net/Dongguabai/article/details/83213721

Java原生API操作ZooKeeper可参看:

Java原生API操作Zookeeper(一)

Java原生API操作Zookeeper(二)

相关内容:

基于Curator操作ZooKeeper(一)-基本操作

基于Curator操作ZooKeeper(二)-Watcher操作-补充TreeCache

基于Curator操作ZooKeeper(二)-Watcher操作

基于Curator操作ZooKeeper(三)-Curator整合Spring

Curator本身已经提供了分布式锁的API,这里是基于节点实现一个简易版本的分布式锁。这个分布式锁其实是存在很大问题的,后续会说明。

application-zookeeper.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

    <description>ZK与Spring整合,启动项目时建立与ZK的连接</description>
    <!--ZK重试策略-->
    <bean id="retryPolicy" class="org.apache.curator.retry.RetryNTimes">
        <!--重试次数-->
        <constructor-arg index="0" value="10"/>
        <!--每次间隔ms-->
        <constructor-arg index="1" value="5000"/>
    </bean>

    <!--ZK客户端-->
    <bean id="client" class="org.apache.curator.framework.CuratorFrameworkFactory" factory-method="newClient"
          init-method="start">
        <!--ZK服务地址,集群使用逗号分隔-->
        <constructor-arg index="0" value="192.168.220.136,192.168.220.137"/>
        <!--session timeout会话超时时间-->
        <constructor-arg index="1" value="10000"/>
        <!--ConnectionTimeoutMs创建连接超时时间-->
        <constructor-arg index="2" value="5000"/>
        <!--重试策略-->
        <constructor-arg index="3" ref="retryPolicy"/>
    </bean>

    <!--扩展注入ZK工具-->
    <bean id="zkCurator" class="dongguabai.ZKCurator" init-method="init">
        <constructor-arg index="0" ref="client"/>
    </bean>
</beans>

ZKCurator:

package dongguabai;

import org.apache.curator.framework.CuratorFramework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Dongguabai
 * @date 2018/10/18 15:12
 */
public class ZKCurator {

    private static final Logger LOGGER = LoggerFactory.getLogger(ZKCurator.class);

    //ZK客户端
    private CuratorFramework client = null;

    public ZKCurator(CuratorFramework client) {
        this.client = client;
    }


    public void init(){
        //使用命名空间
        client  = client.usingNamespace("testDgb");
    }


    public boolean isZKAlive(){
        return client!=null && client.isStarted();
    }
}

DistributedLock:

package dongguabai;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;

/**
 * @author Dongguabai
 * @date 2018/10/20 15:24
 */
public class DistributedLock {

    private static final Logger LOG = LoggerFactory.getLogger(DistributedLock.class);

    //分布式锁总节点名称
    private static final String ZK_LOCK_PROJECT = "locks";
    //分布式锁节点,主要是分类的作用
    private static final String DISTRIBUTED_LOCK = "test1_locks";

    private static CountDownLatch cdl = new CountDownLatch(1);

    private CuratorFramework client = null;

    public DistributedLock(CuratorFramework client) {
        this.client = client;
    }

    public void init() {
        //命名空间
        client = client.usingNamespace("Locks-namespace");
        try {
            if (client.checkExists().forPath("/" + ZK_LOCK_PROJECT) == null) {
                client.create()
                        //可以有,也可以没有递归创建,反正是单节点
                        .creatingParentsIfNeeded()
                        .withMode(CreateMode.PERSISTENT)
                        .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                        .forPath("/" + ZK_LOCK_PROJECT);
            }
            //针对ZK的分布式锁节点,创建相应的Watcher事件
            addWatcherToLock("/" + ZK_LOCK_PROJECT);
        } catch (Exception e) {
            LOG.error("客户端连接ZooKeeper服务器错误,请重试!!!!!!",e);
        }

    }

    /**
     * 获得锁
     */
    public void getLock() {
        System.out.println(Thread.currentThread().getName()+"尝试获取锁");
        //使用死循环,当且仅当上一个锁释放并且当前请求获得锁成功后才会跳出
        while (true) {
            try {
                client.create()
                        .creatingParentsIfNeeded()
                        .withMode(CreateMode.EPHEMERAL)
                        .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                        .forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK);
                LOG.error("获得锁成功!!!!!!");
                return;
            } catch (Exception e) {
                LOG.error("获得锁失败。。。。。。", e);
                try {
                    //如果没有获得到锁,需要重新设置同步资源值
                    if (cdl.getCount() <= 0) {
                        cdl = new CountDownLatch(1);
                    }
                    //阻塞
                    cdl.await();
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    /**
     * 注册事件
     *
     * @param path
     * @throws Exception
     */
    private void addWatcherToLock(String path) throws Exception {
        final PathChildrenCache cache = new PathChildrenCache(client, path, true);
        cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        cache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {
                    String path = event.getData().getPath();
                    LOG.error("上一个会话已经释放锁或者会话已断开,节点路径为:{}", path);
                    if (path.contains(DISTRIBUTED_LOCK)) {
                        LOG.error("释放计数器,让当前请求来获得分布式锁。。。");
                        cdl.countDown();
                    }
                }
            }
        });
    }

    /**
     * 释放锁
     *
     * @return
     */
    public boolean releaseLock() {
        try {
            if (client.checkExists().forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK) != null) {
                client.delete().forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        LOG.error("分布式锁释放完毕。。。");
        return true;
    }
}

Controller:

import dongguabai.DistributedLock;
import dongguabai.ZKCurator;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Dongguabai
 * @date 2018/10/20 17:03
 */
@RestController
public class ZKController {

    private volatile AtomicInteger stock = new AtomicInteger(10);

    @Autowired
    private DistributedLock distributedLock;

    @RequestMapping("sell")
    public Object sell(int buyCount) {
        System.out.println(Thread.currentThread().getName()+"[进入Controller---------------");

        //获取锁
        distributedLock.getLock();

        if (stock.get()< buyCount){
            System.out.println("库存不足,销售数量为:"+ buyCount +",剩余库存为:"+stock.get());
            distributedLock.releaseLock();
            return ResultHelper.success("库存不足,销售数量为:"+ buyCount +",剩余库存为:"+stock.get());
   }

        //模拟复杂业务逻辑
        try {
            Thread.sleep(5000);
            //减少库存
            stock.addAndGet(-1*buyCount);
        } catch (InterruptedException e) {
            distributedLock.releaseLock();
            e.printStackTrace();
            return ResultHelper.success("操作失败");
        }
        System.out.println("准备释放锁--------------");
        distributedLock.releaseLock();
        return true;

    }

    @Autowired
    private ZKCurator zkCurator;
    @RequestMapping("/check")
    public Object check() {
        return ResultHelper.success(zkCurator.isZKAlive() ? "已连接" : "断开");
    }


    @RequestMapping("/test")
    public Object aa() {
        distributedLock.getLock();
        distributedLock.getLock();
        distributedLock.getLock();
        return ResultHelper.success(zkCurator.isZKAlive() ? "已连接" : "断开");
    }
    @RequestMapping("/reset")
    public Object reset() {
        stock = new AtomicInteger(10);
        return ResultHelper.success(zkCurator.isZKAlive() ? "已连接" : "断开");
    }

    @RequestMapping("/demo")
    public Object demo() throws InterruptedException {
        System.out.println(new Date().toLocaleString()+"]]]]"+Thread.currentThread().getName()+"进来了==========");

        Thread.sleep(5000);
        System.out.println(new Date().toLocaleString()+"]]]]"+Thread.currentThread().getName()+"出去了==========");

        return ResultHelper.success(zkCurator.isZKAlive() ? "已连接" : "断开");
    }
}

猜你喜欢

转载自blog.csdn.net/Dongguabai/article/details/83213721
今日推荐