zookeeper的分布式锁机制:

原理:zookeeper的数据结构包含4种:

PERSISTENT         持久化节点

PERSISTENT_SEQUENTIAL   持久化顺序节点

EPHEMERAL         临时节点

EPHEMERAL_SEQUENTIAL   临时顺序节点

 

顺序节点代表create后,zookeeper会自动在后面加上序号,自动加1;临时节点代表客户端连接中断后,该节点自动删除;

利用临时顺序节点的特性,每个要获取锁的客户端(线程)在特定目录下新建一个临时顺序节点,然后比较自己的节点是否是当前路径下序号最小的节点,若是 得获取到锁,若不是,则等待;

释放锁即删除该临时顺序节点或断开连接(临时节点自动被删除);

 

curator提供了InterProcessLock接口,实现了锁机制;

以InterProcessMultiLock实现为例,InterProcessMultiLock是多锁对象,可以对多个对象进行加锁;

InterProcessMutex为可重入锁

/**
 * Created by chenhao on 2018-05-10.
 */
public class ZKLock {
    private static final String CONNECTSTRING = "192.168.0.16:2181";
    private static final String LOCKPATH = "/pay/lock";
    private static final List<String> list = new LinkedList();
    private static CuratorFramework client = CuratorFrameworkFactory.builder().connectString(CONNECTSTRING).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();


    public static void main(String[] args) throws IOException, InterruptedException {
        client.start();
        list.add(LOCKPATH);
        final InterProcessMultiLock lock = new InterProcessMultiLock(client, list);

        for(int i=0;i<1;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.acquire();
                        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss:SSS");
                        System.out.println(sdf.format(new Date()));
                        lock.release();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }


}

  

acquire()为锁方法,这是默认不带参数的方法,当然还有超时自动放弃获取锁的入参方法;

release()为释放锁; 

 

 

acquire()方法

执行过程:①使用UUID生成一个节点名称——②使用client创建生成的节点——③获取该路径下的全部节点名称——④对节点名称排序——⑤判断新建的节点索引是否为第0(第一个节点)——⑥若是则返回节点路径(获取到锁),若不是则添加zookeeper的监听器监听节点状态并阻塞指定时间(由入参决定)

 

方法入口:

执行InterProcessMutex类的

 String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());

是开始获取锁,如果获取到了则返回创建的临时顺序节点路径并保存下来(释放锁时使用);

若获取不到则会阻塞,直到获取锁;

 

 ourPath = driver.createsTheLock(client, path, localLockNodeBytes);  //①②
 hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath);  //③④⑤⑥

 

 

①使用UUID生成一个节点名称

调用LockInternals类的ourPath = driver.createsTheLock(client, path, localLockNodeBytes);方法来生成ourPath

 

 

 

接着调用了下图的方法生成了UUID,然后拼接了"-lock-"在结尾

图中为CreateBuilderImpl类的方法

 

 ②使用client创建生成的节点

使用传入client创建生成的节点,路径为上一步拼接好的

lockNodeBytes为结点数据,没实际作用为空即可;

 

③获取该路径下的全部节点名称

 

 List<String> children = getSortedChildren();获取到父节点下的全部子节点名称;

 

④对节点名称排序

 

排序时从"lock-"到节点名称结束的字符串截取为顺序节点的序号,所以节点名称必须要带有"lock-"

 

 ⑤判断新建的节点索引是否为第0(第一个节点)

 

ourIndex来排序,判断是否为0,为0即获取到锁;

 

⑥若是则返回节点路径(获取到锁),若不是则添加zookeeper的监听器监听节点状态并阻塞指定时间(由入参决定)

 

 

其它:

"lock-"定义的地方

 

posted on 2018-05-10 16:47  唯忆学长  阅读(881)  评论(0编辑  收藏  举报