基本思路:
1.每次请求都在某个根节点下创建一个子节点
2.判断当前请求的节点是否是最小的一个节点。
2.1如果是,说明加锁成功,继续业务;如果不是,说明获取锁失败
2.2如果不是,获取比当前小一个的节点(假设为A),监听A这个节点的状态
2.3当A这个节点的状态为被删除时,说明上个业务处理完毕,则当前获得锁
代码实现
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
ExponentialBackoffRetry retry = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("localhost:2181,localhost:2182,localhost:2183")
.sessionTimeoutMs(30000)
.connectionTimeoutMs(30000)
.retryPolicy(retry)
//.namespace("distributedLock") //每个业务对应的独立命名空间
.build();
client.start();
Thread.sleep(1000);
client.delete().deletingChildrenIfNeeded().forPath("/distributedLock");
String myZnode = client.create()
.creatingParentContainersIfNeeded()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath("/distributedLock/locktest&subject1_lock_", new byte[0]);
client.getChildren().forPath("/distributedLock").forEach(e -> System.out.println("------------------\r\n"+e));
String mySecondZnode = client.create()
.creatingParentContainersIfNeeded()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath("/distributedLock/locktest&subject1_lock_", new byte[1]);
ArrayList<String> list = new ArrayList<>();
client.getChildren().forPath("/distributedLock").forEach(e -> {
list.add(e);
});
Collections.sort(list);
//获取比自己小一个的节点,并监听这个节点,当这个节点被删除池,则当前节点可以获得锁
String substring = mySecondZnode.substring(mySecondZnode.lastIndexOf("/") + 1);
String waitNode = list.get(Collections.binarySearch(list, substring) - 1);
Stat stat = client.checkExists().forPath("/distributedLock/"+waitNode);
if (stat != null){
NodeCache cache = new NodeCache(client, "/distributedLock/"+waitNode);
NodeCacheListener listener = () -> {
ChildData data = cache.getCurrentData();
if (null != data) {
System.out.println("节点数据:" + new String(cache.getCurrentData().getData()));
} else {
System.out.println("节点被删除!");
countDownLatch.countDown();
}
};
cache.getListenable().addListener(listener);
cache.start();
Runnable runnable = () -> {
try {
Thread.sleep(60000);
client.delete().withVersion(-1).forPath("/distributedLock/"+waitNode);
System.out.println("=============节点删除成功============");
} catch (Exception e) {
e.printStackTrace();
}
};
new Thread(runnable).start();
countDownLatch.await();
cache.close();
client.close();
System.out.println("---------------哈哈哈哈,我是第一,我获得锁了-------------------");
}
pom依赖见github(mysql读写分离和zookeeper锁都写在一个项目里面,懒得分开了):github.com/zhankun/mys…