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-"定义的地方