• 分布式锁-基于ZK和Redis实现


    一、基于zookeeper实现分布式锁

    1.1 Zookeeper的常用接口

    package register;
    
    
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.WatchedEvent;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.Watcher.Event.KeeperState;
    import org.apache.zookeeper.ZooDefs.Ids;
    import org.apache.zookeeper.ZooKeeper;
    import org.apache.zookeeper.data.Stat;
    
    public class BaseZookeeper implements Watcher{
    
        public BaseZookeeper(){}
    
        public BaseZookeeper(String host){
            this.connectZookeeper(host);
        }
    
        private ZooKeeper zookeeper;
    
        //超时时间
        private static final int SESSION_TIME_OUT = 2000;
        private CountDownLatch countDownLatch = new CountDownLatch(1);
    
        public void process(WatchedEvent event) {
            if (event.getState() == KeeperState.SyncConnected) {
                //System.out.println("Watch received event");
                countDownLatch.countDown();
            }
        }
    
        //连接zookeeper
        protected void connectZookeeper(String host){
            try {
                zookeeper = new ZooKeeper(host, SESSION_TIME_OUT, this);
                countDownLatch.await();
                //System.out.println("zookeeper connection success");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        //创建节点
        protected String createNode(String path, String data){
            try {
                //永久节点
                String result = this.zookeeper.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                //临时节点(会话关闭就删除了,调用close后就自动删除了)
                //String result = this.zookeeper.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                System.out.println("createNode: " + result);
                return result;
            } catch (Exception e) {
                //e.printStackTrace();
                return null;
            }
        }
    
        //创建多级节点
        //String path = "/dubbo/server/com.wzy.server.OrderServer";
        protected boolean createMultNode(String path){
    
            String[] paths = path.split("/");
            String realPath = "/";
            for (int i=1; i<paths.length; i++) {
                realPath += paths[i];
                String result = createNode(realPath, "");
    
                if (result == null) {
                    return false;
                }
                realPath += "/";
            }
            return true;
        }
    
        //获取路径下所有子节点
        protected List<String> getChildren(String path){
            try {
                List<String> children = zookeeper.getChildren(path, false);
                return children;
            } catch (Exception e) {
                //当路径已经是根节点(没有子节点)时,就会抛异常
                return null;
            }
    
        }
    
        //获取节点上面的数据
        protected String getData(String path) throws KeeperException, InterruptedException{
            byte[] data = zookeeper.getData(path, false, null);
            if (data == null) {
                return "";
            }
            return new String(data);
        }
    
        //设置节点信息
        protected Stat setData(String path, String data){
            try {
                getData(path);
                Stat stat = zookeeper.setData(path, data.getBytes(), -1);
                return stat;
            } catch (Exception e) {
                //String result = createNode(path,"");
                return null;
            }
    
        }
    
        //删除节点
        protected boolean deleteNode(String path){
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            try {
                zookeeper.delete(path, -1);
            } catch (InterruptedException e) {
                return false;
            } catch (KeeperException e) {
                return false;
            }
            return true;
        }
    
        //获取创建时间
        protected String getCTime(String path) throws KeeperException, InterruptedException{
            Stat stat = zookeeper.exists(path, false);
            return String.valueOf(stat.getCtime());
        }
    
        //获取某个路径下孩子的数量
        protected Integer getChildrenNum(String path) throws KeeperException, InterruptedException{
            int childenNum = zookeeper.getChildren(path, false).size();
            return childenNum;
        }
    
        //监听节点是否被删除
        protected void watchIsDel(final String path) throws Exception{
            zookeeper.exists(path, new Watcher() {
                public void process(WatchedEvent watchedEvent) {
                    Event.EventType type = watchedEvent.getType();
                    if (Event.EventType.NodeDeleted.equals(type)) {
                        System.out.println("结点 " + path + "被删除了");
                    }
                }
            });
        }
    
        //关闭连接
        public void closeConnection() {
            if (zookeeper != null) {
                try {
                    zookeeper.close();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    package register;
    
    import framework.URL;
    
    import java.util.List;
    import java.util.Random;
    
    /**
     * zk 的注册工具
     */
    public class ZkRegister extends BaseZookeeper {
    
        private static String ZK_HOST = "127.0.0.1:2181";
        private final String SERVER_ADDRESS = "/dubbo/server";
        private final String ROOT_ADDRESS = "/dubbo";
    
        public ZkRegister(){
            super(ZK_HOST);
        }
    
        public void setZkHost(String host){ZkRegister.ZK_HOST = host;}
    
        /**
         * 注册服务
         * @param serverInterface
         * @param url
         * @return
         */
        public boolean regist(Class serverInterface, URL url){
            if (null != getChildren(ROOT_ADDRESS)){
                deleteNodeRF(ROOT_ADDRESS);
            }
            return addAddressToNode(SERVER_ADDRESS + "/" + serverInterface.getName(), new String[]{url.getAddress()});
        }
    
        /**
         * 从地址列表里随机获取一个地址
         * @param serverInterface
         * @return
         */
        public String getURLRandom(Class serverInterface){
            List<String> urls = getChildren(SERVER_ADDRESS + "/" + serverInterface.getName());
            return urls.get(new Random().nextInt(urls.size()));
        }
    
        /**
         * 向节点添加服务地址
         * @param nodePath
         * @param address
         * @return
         * String path = "/dubbo/server/com.wzy.server.OrderServer";
         * String[] ip = new String[]{"192.168.37.1","192.168.37.2","192.168.37.3"};
         */
        public boolean addAddressToNode (String nodePath, String[] address) {
            if (!nodePath.startsWith("/")) {
                nodePath = "/" + nodePath;
            }
    
            if (null == getChildren(nodePath)){
                createMultNode(nodePath);
            }
            for (int i=0; i<address.length; i++) {
                String newPath = nodePath + "/" + address[i];
                String result = createNode(newPath,"");
                if (null == result) {
                    return false;
                }
            }
            return true;
        }
    
        public boolean deleteNodeRF (String rootPath) {
            return deleteNodeRF(rootPath, rootPath);
        }
        /**
         * 删除节点及其子目录
         * @param rootPath
         * @return
         */
        private boolean deleteNodeRF (String rootPath, String parentPath) {
            if (!rootPath.startsWith("/")) {
                rootPath = "/" + rootPath;
            }
            List<String> childs = getChildren(rootPath);
            if (childs.size() > 0) {
                //递归
                for (String child : childs) {
                    deleteNodeRF(rootPath + "/" + child, rootPath);
                }
            } else {
                System.out.println("delete: " + rootPath + " " + deleteNode(rootPath));
            }
            System.out.println("delete: " + parentPath + " " + deleteNode(parentPath));
    
            return true;
        }
    }

    1.2 基于zk实现分布式锁

    package lock;
    
    import org.apache.zookeeper.*;
    
    import java.util.Collections;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    /**
     * Zookeeper实现分布式锁
     */
    public class ZookeeperLock implements Lock {
    
        private ThreadLocal<ZooKeeper> zk = new ThreadLocal<ZooKeeper>();
        private String host = "localhost:2181";
    
        private final String LOCK_NAME = "/LOCK";
        private ThreadLocal<String> CURRENT_NODE = new ThreadLocal<String>();
    
        private void init() {
            if (zk.get() == null) {
                synchronized (ZookeeperLock.class) {
                    if (zk.get() == null) {
                        try {
                            zk.set( new ZooKeeper(host, 2000, new Watcher() {
                                public void process(WatchedEvent watchedEvent) {
                                    // do nothing..
                                }
                            }));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
    
                    }
                }
            }
        }
    
        public void lock() {
            init();
            if (tryLock()) {
                System.out.println("get lock success");
            }
        }
    
        public boolean tryLock() {
            String node = LOCK_NAME + "/zk_";
            try {
                //创建临时顺序节点  /LOCK/zk_1
                CURRENT_NODE.set(zk.get().create(node, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL));
                //zk_1,zk_2
                List<String> list = zk.get().getChildren(LOCK_NAME, false);
                Collections.sort(list);
                System.out.println(list);
                String minNode = list.get(0);
    
                if ((LOCK_NAME + "/" + minNode).equals(CURRENT_NODE.get())) {
                    return true;
                } else {
                    //等待锁
                    Integer currentIndex = list.indexOf(CURRENT_NODE.get().substring(CURRENT_NODE.get().lastIndexOf("/") + 1));
                    String preNodeName = list.get(currentIndex - 1);
    
                    //监听前一个节点删除事件
                    final CountDownLatch countDownLatch = new CountDownLatch(1);
                    zk.get().exists(LOCK_NAME + "/" + preNodeName, new Watcher() {
                        public void process(WatchedEvent watchedEvent) {
                            if (Event.EventType.NodeDeleted.equals(watchedEvent.getType())) {
                                countDownLatch.countDown();
                                System.out.println(Thread.currentThread().getName() + "唤醒锁..");
                            }
                        }
                    });
    
                    System.out.println(Thread.currentThread().getName() + "等待锁..");
                    countDownLatch.await();//在变成0之前会一直阻塞
    
                }
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
    
            return true;
        }
    
        public void unlock() {
            try {
                zk.get().delete(CURRENT_NODE.get(), -1);
                CURRENT_NODE.remove();
                zk.get().close();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (KeeperException e) {
                e.printStackTrace();
            }
    
        }
    
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        public void lockInterruptibly() throws InterruptedException {
    
        }
    
        public Condition newCondition() {
            return null;
        }
    }

    二、基于Redis实现分布式锁

    package lock;
    
    import redis.clients.jedis.Jedis;
    
    import java.util.Collections;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    /**
     * Redis实现分布式锁
     */
    public class RedisLock implements Lock {
    
        ThreadLocal<Jedis> jedis = new ThreadLocal<Jedis>();
    
        private static String LOCK_NAME = "LOCK";
        private static String REQUEST_ID = null;
    
        public RedisLock (String requestId) {
            RedisLock.REQUEST_ID = requestId;
            if (jedis.get() == null) {
                jedis.set(new Jedis("localhost"));
            }
        }
        public void lock() {
            if (tryLock()) {
                //jedis.set(LOCK_NAME, REQUEST_ID);
                //jedis.expire(LOCK_NAME, 1000);//设置过期时间
    
                //问题:上面两句代码不存在原子性操作,所以用下面一句代码替换掉
                jedis.get().set(LOCK_NAME, REQUEST_ID, "NX", "PX", 1000);
            }
        }
    
        public boolean tryLock() {
            while (true) {
                //key不存在时设置值,存在则设置失败。设置成功返回1,设置失败返回0
                Long lock = jedis.get().setnx(LOCK_NAME, REQUEST_ID);
                if (lock == 1) {
                    return true;
                }
            }
        }
    
    
        public void unlock() {
            //问题:保证不了原子性
            //String value = jedis.get(LOCK_NAME);
            //if (REQUEST_ID.equals(value)) {
            //    jedis.del(LOCK_NAME);
            //}
    
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            jedis.get().eval(script, Collections.singletonList(LOCK_NAME), Collections.singletonList(REQUEST_ID));
            jedis.get().close();
            jedis.remove();
    
        }
    
        public Condition newCondition() {
            return null;
        }
    
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        public void lockInterruptibly() throws InterruptedException {
    
        }
    }
  • 相关阅读:
    uniGUI学习之ExtJS之xtype容器内创建元素的简便方法(52)
    uniGUI学习之ExtJS事件(51)
    uniGUI学习之自定义CSS之如何找控件属性(50)
    uniGUI学习之自定义CSS(49)
    CANoe CAPL 之 Checksum and Counter
    PYTHON3 之 IVI的CAN总线自动化测试
    CAP原则(CAP定理)、BASE理论
    iOS 项目运行pod install报错 could not find compatible versions
    Xcode 11.7 安装报Failed to find a suitable device for the type IBSimDeviceTypeiPad2x
    Flutter 异常捕获框架catcher
  • 原文地址:https://www.cnblogs.com/wwzyy/p/10769207.html
Copyright © 2020-2023  润新知