基于zookeeper分布式锁的解决方案

1. 概述

分布式锁,如果你有多个机器在访问同一个共享资源,

那么这个时候,如果你需要加个锁,让多个分布式的机器在访问共享资源的时候串行起来

那么这个时候,那个锁,多个不同机器上的服务共享的锁,就是分布式锁

分布式锁当然有很多种不同的实现方案,redis分布式锁,zookeeper分布式锁

2. 原理

我们通过去创建zk的一个临时node,来模拟加锁

zk会给你保证说,只会创建一个临时node,其他请求过来如果再要创建临时node,就会报错,NodeExistsException

那么所以说,我们的所谓上锁,其实就是去创建某个临时node

如果临时node创建成功了,那么说明我们成功加锁了,此时就可以去执行业务操作

如果临时node创建失败了,说明有人已经在拿到锁了,在进行业务操作,那么就不断的等待,直到自己可以获取到锁为止

释放一个分布式锁,去删除掉那个临时node就可以了,就代表释放了一个锁,

那么此时其他的机器就可以成功创建临时node,获取到锁

3. 应用场景

3.1 缓存重建

1、主动更新

监听消息队列,后台修改数据,获取到一个缓存变更的消息之后,去哪个源服务中调用接口拉取数据,更新到ehcache和redis中

先获取分布式锁,然后才能更新redis,同时更新时要比较时间版本

2、被动重建

直接读取源头数据,直接返回给nginx,同时推送一条消息到一个队列,后台线程异步消费

后台线程负责先获取分布式锁,然后才能更新redis,同时要比较时间版本

4. zk分布式锁的解决并发冲突的方案

(1)变更缓存重建以及空缓存请求重建,更新redis之前,都需要先获取对应的分布式锁

(2)拿到分布式锁之后,需要根据时间版本去比较一下,如果自己的版本新于redis中的版本,那么就更新,否则就不更新

(3)如果拿不到分布式锁,那么就等待,不断轮询等待,直到自己获取到分布式的锁

5. 封装类

/**
 * @author kris
 * @create 2018-08-05 上午 11:42
 */
public class ZooKeeperSession {

private static CountDownLatch connectedSemaphore = new CountDownLatch(1);

private ZooKeeper zookeeper;

public ZooKeeperSession() {
    //去连接zookeeper server,创建会话的时候,是异步去进行的
    //所以要给一个监听器,说告诉我们什么时候才是真正完成了跟zk server的连接

    try {
        this.zookeeper = new ZooKeeper("192.168.92.132:2181,192.168.92.133:2181,192.168.92.134:2181", 50000, new ZooKeeperWatcher());
        //给一个状态connecting,连接中
        System.out.println(zookeeper.getState());

        // CountDownLatch
        // java多线程并发同步的一个工具类
        // 会传递进去一些数字,比如说1,2 ,3 都可以
        // 然后await(),如果数字不是0,那么久卡住,等待

        // 其他的线程可以调用coutnDown(),减1
        // 如果数字减到0,那么之前所有在await的线程,都会逃出阻塞的状态
        // 继续向下运行

        connectedSemaphore.await();

        System.out.println("ZooKeeper session established......");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * 获取分布式锁
 *
 * @param productId
 */
public void  acquireDistributedLock(Long productId) {
    String path = "/product-lock-" + productId;

    try {
        zookeeper.create(path, "".getBytes(),
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        System.out.println("success to acquire lock for product[id=" + productId + "]");
    } catch (Exception e) {
        //如果那个商品对应的锁的node,已经存在了,就是已经被别人加锁了,那么这里就会报错
        //NodeExistsException
        int count = 0;
        while (true) {
            try {
                Thread.sleep(20);
                zookeeper.create(path, "".getBytes(),
                        ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            } catch (Exception e1) {
                e1.printStackTrace();
                count++;
                continue;
            }
            System.out.println("success to acquire lock for product[id=" + productId + "] after " + count + " times try......");
            break;
        }
    }
}

/**
 * 释放掉一个分布式锁
 *
 * @param productId
 */
public void releaseDistributedLock(Long productId) {
    String path = "/product-lock-" + productId;
    try {
        zookeeper.delete(path, -1);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * 封装单例的静态内部类
 */
private static class Singleton {
    private static ZooKeeperSession instance;

    static {
        instance = new ZooKeeperSession();
    }

    public static ZooKeeperSession getInstance() {
        return instance;
    }

}

public static ZooKeeperSession getInstance() {
    return Singleton.getInstance();
}

/**
 * 建立zk session的watcher
 */
private class ZooKeeperWatcher implements Watcher {
    @Override
    public void process(WatchedEvent watchedEvent) {
        System.out.println("Receive watched event: " + watchedEvent.getState());
        if (Event.KeeperState.SyncConnected == watchedEvent.getState()) {
            connectedSemaphore.countDown();
        }
    }
}

}

猜你喜欢

转载自blog.csdn.net/qq_25868207/article/details/81430493