redisson zookeeper实现分布式锁优缺点

CAP指的是 一致性(Consistency) ,可用性(Availability), 分区容错性(Partition tolerance)
eureka ap zookeeper cp redis cp
Eureak选择AP 保证了可用性降低了一致性 , Zookeeper 就是 CP ; Redis AP ; Nacos 默认 AP ,可以 CP和AP可以切换

可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,也要释放多少次 ReentrantLock和sychronized是可重入锁 拿到锁情况下就是不需要排队就可以在此拿到锁
!!而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁
公平锁依次顺序 非公平锁

//可以看看源码
RLock redissonLock = redissonClient.getLock(“redissonLock”);
//参数:等待时间,过期时间,时间单位
boolean lockFlag = redissonLock.tryLock(1, 10, TimeUnit.SECONDS);
if (lockFlag) {
try {
//业务逻辑
} finally {
redissonLock.unlock();
}
}

Redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。Zookeeper分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小
Redis获取锁的那个客户端bug了或者挂了,那么只能等待超时时间之后才能释放锁;而Zookeeper的话,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁
redis的性能高于zk太多了,可在可靠性上又远远不如zk redis锁响应更快,对并发的支持性能更好
//启动客户端
zkCli.sh -server 127.0.0.1:2181
ls get set delete
Znode分为四种类型: 持久节点 默认(PERSISTENT)持久节点顺序节点(PERSISTENT_SEQUENTIAL) 临时节点(EPHEMERAL)与zookeeper断开连接后,临时节点会被删除
公平锁:顺序临时节点!!,只需要注册个检测器在上一个节点 不会引起羊群效应!!
非公平锁:抢占式 都只创建一个节点!!
死锁问题:出现异常情况下,没来得及删除节点,锁将一直被占用无法释放 惊群效应:可以看到输出日志里面,有很大比例都是删除回调事件。原因是所有服务实例同时监听此节点,每次的释放锁删除节点都会触发回调事件,而所有等待锁的服务实例都会接收回调,并执行抢占代码,那么将造成通信,服务器资源不必要的浪费

zookeeper锁的过程就是先创建一个永久节点,在其下面创建临时有序子节点,并且在前一个节点上创建监听事件 占用锁节点被删除后再去递归!getLock

 
     com.101tec
     zkclient
     0.10
 

public class ZookeeperTest {
public static void main(String[] args) throws InterruptedException {
List threadList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(new Runnable() {
@Override
public void run() {
ZookeeperClient zkLock = new ZookeeperClient();
zkLock.getLock();
try {
Thread.sleep(1*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

                zkLock.unlock();
            }
        }));
    }

    threadList.forEach(thread -> thread.start());

    Thread.sleep(1000000);
}

}
class ZookeeperClient{

// 锁节点路径
final String LOCK_PATH = "/Lock";
// 当前锁节点路径
private String currentPath;
// 前一个锁节点路径
private String prevPath;
ZkClient zkClient = new ZkClient("192.168.31.30", 45 * 1000);//指的是获取锁后的连接时间

public void getLock() {
    System.out.println("getLock");
    // 尝试获取锁
    if (tryLock()) {
        System.out.println(Thread.currentThread().getName() + " 获取锁成功");
    } else {
        // 等待锁
        waitLock();
        // 再次获取锁
        getLock();
    }
}

public boolean tryLock() {
    //先创建锁节点路径
    if (!zkClient.exists(LOCK_PATH)) {
        zkClient.createPersistent(LOCK_PATH);
    }
    if (currentPath == null) {
        //创建有序节点
        currentPath = zkClient.createEphemeralSequential(LOCK_PATH + "/", "testdata");
        System.out.println("创建节点:" + currentPath);
    }
    List childrenList = zkClient.getChildren(LOCK_PATH);
    Collections.sort(childrenList);//会导致有序123

    if (currentPath.equals(LOCK_PATH + "/" + childrenList.get(0))) {
        return true;
    } else {
        int currentIndex = childrenList.indexOf(currentPath.substring(LOCK_PATH.length() + 1));
        prevPath = LOCK_PATH + "/" + childrenList.get(currentIndex - 1);
        return false;
    }
}

public void waitLock() {
    CountDownLatch countDownLatch = new CountDownLatch(1);
    IZkDataListener listener = new IZkDataListener() {
        @Override
        public void handleDataChange(String s, Object o) {
        }

        @Override
        public void handleDataDeleted(String s) {
            System.out.println("收到节点:" + s + "被删除的消息");
            countDownLatch.countDown();
        }
    };

    // 注册监听器
    zkClient.subscribeDataChanges(prevPath, listener);

    // 检查该节点是否存在
    if (zkClient.exists(prevPath)) {
        try {
            // 阻塞等待节点的删除事件
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    // 解除监听
    zkClient.unsubscribeDataChanges(prevPath, listener);
}
public void unlock() {
    // 释放锁
    releaseLock();
}

public void releaseLock() {
    zkClient.delete(currentPath);
}

}

你可能感兴趣的:(框架,zookeeper,分布式,redis)