什么是分布式共享锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。
分布式共享锁的应用场景
在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
实现原理
实现分布式锁必须要依靠第三方存储介质来存储锁的元数据等信息。这里我们利用Zookeeper可以对临时有序设置监听,当临时有序节点的子节点发生变化时,可以收到回调,在回调中判断是否获取到了锁,如果获取到了锁就执行业务逻辑,否者进行等待。利用Zookeeper实现分布式共享锁的好处不仅面对高负载请求毫无压力,同时某一台宕机毫不影响分布式锁数据一致性,且附带了监听机制,当某一程序释放某一个锁后,其他程序可以及时得到通知来获得对该分布式锁的控制权,这里的轮询实现不需要我们去开发了。
实现步骤
添加依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
<!--<type>pom</type>-->
</dependency>
代码
package cn.zhou.bigdata.zklock;
import org.apache.zookeeper.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class DistributedLock {
private static ZooKeeper zkClient;
private static String connectionStr = "mini41:2181,mini42:2181,mini43:2181";
private static int sessionWaitTime = 2000;
private static final String PARENT_NODE_NAME = "/lock";
private static String createdNodeName;
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
DistributedLock distributedLock = new DistributedLock();
distributedLock.getConnection();
Thread.sleep(Integer.MAX_VALUE);
}
private void getConnection() throws IOException, KeeperException, InterruptedException {
zkClient = new ZooKeeper(connectionStr, sessionWaitTime, new Watcher() {
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeChildrenChanged && PARENT_NODE_NAME.equals(watchedEvent.getPath())) {
try {
List<String> children = zkClient.getChildren(PARENT_NODE_NAME, true);
Collections.sort(children);
String nodeName = createdNodeName.substring((PARENT_NODE_NAME+"/").length());
if (children.indexOf(nodeName) == 0) {
//访问共享资源处理业务,并且在处理完成之后删除锁
doBusiness();
unlock();
createLock();
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
createLock();
Thread.sleep(new Random().nextInt(2000));
List<String> children = zkClient.getChildren(PARENT_NODE_NAME, true, null);
if (children != null && children.size() == 1) {
doBusiness();
unlock();
createLock();
}
}
private static void doBusiness() {
System.out.println("start doing business");
try {
Thread.sleep(20000);
System.out.println("finished: " + createdNodeName);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 释放锁
private static void unlock() throws KeeperException, InterruptedException {
zkClient.delete(createdNodeName, -1);
System.out.println("unlock--- delete znode : " + createdNodeName);
}
// 创建锁
private static void createLock() throws KeeperException, InterruptedException {
createdNodeName = zkClient.create(PARENT_NODE_NAME + "/" + "lockedServer", "mini1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("createLock--- createdNodeName = " + createdNodeName);
}
}
测试
启动多个DistributedLock的main方法
启动第一个
由于只有一个程序,发现只有自己,然后第一个程序获取到了锁,执行自己的业务逻辑。
启动第二个
第二个程序注册了一把锁,等待获取锁。
启动第三个
第三个程序注册了一把锁,等待获取锁。
第二个程序日志
第二个程序获取到了锁,执行业务逻辑,然后释放锁,重新注册一把锁,等待下一次获取锁。
所有程序都做如下循环:先注册锁,等待获取锁,获取到锁,执行业务逻辑,执行完之后释放锁,然后再注册锁,等待获取锁…
同一时间只有一个程序获取锁,访问公共资源。