Java原生API操作ZooKeeper可参看:
Java原生API操作Zookeeper(一)
Java原生API操作Zookeeper(二)
相关内容:
基于Curator操作ZooKeeper(一)-基本操作
基于Curator操作ZooKeeper(二)-Watcher操作-补充TreeCache
基于Curator操作ZooKeeper(二)-Watcher操作
基于Curator操作ZooKeeper(三)-Curator整合Spring
Curator本身已经提供了分布式锁的API,这里是基于节点实现一个简易版本的分布式锁。这个分布式锁其实是存在很大问题的,后续会说明。
application-zookeeper.xml:
ZK与Spring整合,启动项目时建立与ZK的连接
ZKCurator:
package dongguabai;
import org.apache.curator.framework.CuratorFramework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Dongguabai
* @date 2018/10/18 15:12
*/
public class ZKCurator {
private static final Logger LOGGER = LoggerFactory.getLogger(ZKCurator.class);
//ZK客户端
private CuratorFramework client = null;
public ZKCurator(CuratorFramework client) {
this.client = client;
}
public void init(){
//使用命名空间
client = client.usingNamespace("testDgb");
}
public boolean isZKAlive(){
return client!=null && client.isStarted();
}
}
DistributedLock:
package dongguabai;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
/**
* @author Dongguabai
* @date 2018/10/20 15:24
*/
public class DistributedLock {
private static final Logger LOG = LoggerFactory.getLogger(DistributedLock.class);
//分布式锁总节点名称
private static final String ZK_LOCK_PROJECT = "locks";
//分布式锁节点,主要是分类的作用
private static final String DISTRIBUTED_LOCK = "test1_locks";
private static CountDownLatch cdl = new CountDownLatch(1);
private CuratorFramework client = null;
public DistributedLock(CuratorFramework client) {
this.client = client;
}
public void init() {
//命名空间
client = client.usingNamespace("Locks-namespace");
try {
if (client.checkExists().forPath("/" + ZK_LOCK_PROJECT) == null) {
client.create()
//可以有,也可以没有递归创建,反正是单节点
.creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath("/" + ZK_LOCK_PROJECT);
}
//针对ZK的分布式锁节点,创建相应的Watcher事件
addWatcherToLock("/" + ZK_LOCK_PROJECT);
} catch (Exception e) {
LOG.error("客户端连接ZooKeeper服务器错误,请重试!!!!!!",e);
}
}
/**
* 获得锁
*/
public void getLock() {
System.out.println(Thread.currentThread().getName()+"尝试获取锁");
//使用死循环,当且仅当上一个锁释放并且当前请求获得锁成功后才会跳出
while (true) {
try {
client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK);
LOG.error("获得锁成功!!!!!!");
return;
} catch (Exception e) {
LOG.error("获得锁失败。。。。。。", e);
try {
//如果没有获得到锁,需要重新设置同步资源值
if (cdl.getCount() <= 0) {
cdl = new CountDownLatch(1);
}
//阻塞
cdl.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}
/**
* 注册事件
*
* @param path
* @throws Exception
*/
private void addWatcherToLock(String path) throws Exception {
final PathChildrenCache cache = new PathChildrenCache(client, path, true);
cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
cache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {
String path = event.getData().getPath();
LOG.error("上一个会话已经释放锁或者会话已断开,节点路径为:{}", path);
if (path.contains(DISTRIBUTED_LOCK)) {
LOG.error("释放计数器,让当前请求来获得分布式锁。。。");
cdl.countDown();
}
}
}
});
}
/**
* 释放锁
*
* @return
*/
public boolean releaseLock() {
try {
if (client.checkExists().forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK) != null) {
client.delete().forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK);
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
LOG.error("分布式锁释放完毕。。。");
return true;
}
}
Controller:
import dongguabai.DistributedLock;
import dongguabai.ZKCurator;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Dongguabai
* @date 2018/10/20 17:03
*/
@RestController
public class ZKController {
private volatile AtomicInteger stock = new AtomicInteger(10);
@Autowired
private DistributedLock distributedLock;
@RequestMapping("sell")
public Object sell(int buyCount) {
System.out.println(Thread.currentThread().getName()+"[进入Controller---------------");
//获取锁
distributedLock.getLock();
if (stock.get()< buyCount){
System.out.println("库存不足,销售数量为:"+ buyCount +",剩余库存为:"+stock.get());
distributedLock.releaseLock();
return ResultHelper.success("库存不足,销售数量为:"+ buyCount +",剩余库存为:"+stock.get());
}
//模拟复杂业务逻辑
try {
Thread.sleep(5000);
//减少库存
stock.addAndGet(-1*buyCount);
} catch (InterruptedException e) {
distributedLock.releaseLock();
e.printStackTrace();
return ResultHelper.success("操作失败");
}
System.out.println("准备释放锁--------------");
distributedLock.releaseLock();
return true;
}
@Autowired
private ZKCurator zkCurator;
@RequestMapping("/check")
public Object check() {
return ResultHelper.success(zkCurator.isZKAlive() ? "已连接" : "断开");
}
@RequestMapping("/test")
public Object aa() {
distributedLock.getLock();
distributedLock.getLock();
distributedLock.getLock();
return ResultHelper.success(zkCurator.isZKAlive() ? "已连接" : "断开");
}
@RequestMapping("/reset")
public Object reset() {
stock = new AtomicInteger(10);
return ResultHelper.success(zkCurator.isZKAlive() ? "已连接" : "断开");
}
@RequestMapping("/demo")
public Object demo() throws InterruptedException {
System.out.println(new Date().toLocaleString()+"]]]]"+Thread.currentThread().getName()+"进来了==========");
Thread.sleep(5000);
System.out.println(new Date().toLocaleString()+"]]]]"+Thread.currentThread().getName()+"出去了==========");
return ResultHelper.success(zkCurator.isZKAlive() ? "已连接" : "断开");
}
}