我们常说的锁是单进程多线程锁,在多线程并发编程中,用于线程之间的数据同步,保护共享资源的访问。而分布式锁,指在分布式环境下,保护跨进程、跨主机、跨网络的共享资源,实现互斥访问,保证一致性。
架构图
左侧是zookeeper集群,locker是数据节点,node_1到node_n代表一系列的顺序节点。
右侧client_1至client_n代表客户端,Service代表需要互斥访问的服务。
总实现思路,是在获取锁的时候在locker节点下创建顺序节点,在释放锁的时候,把自己创建的节点删除。
流程图
1、新建zookeeper-lock工程
Lock.class:
public class Lock {
private Logger logger = LoggerFactory.getLogger(getClass());
private ZkClient zkClient = null;
private int SESSIONTIMEOUT = 25000;
private int CONNECTIONTIMEOUT = 25000;
public Lock() {
}
public Lock(String ipAddress) {
this.zkClient = new ZkClient(ipAddress, SESSIONTIMEOUT, CONNECTIONTIMEOUT, new SerializableSerializer());
}
public void start() {
logger.info("Lock 服务启动,准备初始化...");
init();
}
public void init() {
logger.info("Lock 开始初始化...");
}
/**
* 创建节点
*
* @param data
*/
public String create(String data) {
boolean exists = zkClient.exists("/lock");
if (!exists) {
zkClient.createPersistent("/lock");
}
String path = "/lock".concat("/node-");
String nodePath = zkClient.createEphemeralSequential(path, data);
logger.info("{}创建{}节点,节点数据是:{}", data, nodePath, data);
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
return nodePath;
}
/**
* 获取锁
*
* @param nodePath
*/
public void lock(String nodePath) {
List<String> list = zkClient.getChildren("/lock");
//升序排序
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
o1 = StringUtils.substringAfter(o1, "-");
o2 = StringUtils.substringAfter(o2, "-");
int num1 = Integer.parseInt(o1);
int num2 = Integer.parseInt(o2);
if (num1 > num2) {
return 1;
} else if (num1 < num2) {
return -1;
} else {
return 0;
}
}
});
logger.info("{}:节点{}的子节点有:{}", nodePath, "/lock", list.toString());
String firtNodePath = "/lock".concat("/").concat(list.get(0));
if (nodePath.equals(firtNodePath)) {
String data = zkClient.readData(nodePath);
logger.info("{}获取了锁", data);
try {
Thread.sleep(10000);
release(nodePath);
} catch (Exception e) {
e.printStackTrace();
}
} else {
String data = zkClient.readData(nodePath);
logger.info("{}等待获取锁", data);
//获取上一个节点的位置信息
String nodePath1 = StringUtils.substringAfterLast(nodePath, "/");
int temp = 0;
for (String string : list) {
if (!string.equals(nodePath1)) {
temp++;
}
}
String previousNodePath = "/lock".concat("/").concat(list.get(temp - 1));
zkClient.subscribeDataChanges(previousNodePath, new IZkDataListener() {
@Override
public void handleDataChange(String s, Object o) throws Exception {
}
@Override
public void handleDataDeleted(String s) throws Exception {
logger.info("{}节点释放了锁", s);
zkClient.unsubscribeDataChanges(s, new IZkDataListener() {
@Override
public void handleDataChange(String s, Object o) throws Exception {
}
@Override
public void handleDataDeleted(String s) throws Exception {
}
});
lock(nodePath);
}
});
}
}
/**
* 释放锁
*
* @param nodePath
*/
public void release(String nodePath) {
logger.info("{}释放锁", nodePath);
zkClient.delete(nodePath);
}
}
WebListener.class:
@Component
public class WebListener implements ServletContextListener {
private Logger logger = LoggerFactory.getLogger(getClass());
private String ipAddress = "192.168.202.128:2181,192.168.202.129:2181,192.168.202.130:2181";
@Override
public void contextInitialized(ServletContextEvent sce) {
Lock lock = new Lock("192.168.202.128:2181");
lock.start();
String client1 = "client1";
String client2 = "client2";
String client3 = "client3";
String nodePath1 = lock.create(client1);
String nodePath2 = lock.create(client2);
String nodePath3 = lock.create(client3);
lock.lock(nodePath1);
lock.lock(nodePath2);
lock.lock(nodePath3);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
2、运行及运行结果: