分布式共享锁的程序逻辑流程
有时根据需求,几台服务器需要从一个WEB接口或者数据库获得同样的资源,因为不能同时获取,就涉及到了资源调度算法
一种资源调度逻辑是:
1.程序节点启动时到zookeeper上注册一个“临时+序号”的znode,并监听父节点。
2.获取父节点下所有的程序子节点,比较序号的大小。
3.序号最小的获取到“锁”,去访问资源,访问完后,删除自己的节点,相当于释放锁,并且重新注册一个新的子节点。
4.其他程序节点会收到事件通知,则可以去zk上获取锁。
上伪代码:
//getConnect() 获取连接 //registerLock(/lock/app.emphemeral_sequential) 注册锁 //getLock(){获取子节点;比较自己的序号是否是最小的,如果是,则返回锁获取成功} //访问资源 //releaseLock(){删除自己的子节点,并创建一个新的子节点} //监听器 process(){getLock(0}
上源代码:
public class DistributedClientLock { // 会话超时 private static final int SESSION_TIMEOUT = 2000; // zookeeper集群地址 private String hosts = "mini1:2181,mini2:2181,mini3:2181"; private String groupNode = "locks"; private String subNode = "sub"; private boolean haveLock = false; private ZooKeeper zk; // 记录自己创建的子节点路径 private volatile String thisPath; /** * 连接zookeeper */ public void connectZookeeper() throws Exception { zk = new ZooKeeper(hosts, SESSION_TIMEOUT, new Watcher() { public void process(WatchedEvent event) { try { // 判断事件类型,此处只处理子节点变化事件 if (event.getType() == EventType.NodeChildrenChanged && event.getPath().equals("/" + groupNode)) { //获取子节点,并对父节点进行监听 List<String> childrenNodes = zk.getChildren("/" + groupNode, true); String thisNode = thisPath.substring(("/" + groupNode + "/").length()); // 去比较是否自己是最小id Collections.sort(childrenNodes); if (childrenNodes.indexOf(thisNode) == 0) { //访问共享资源处理业务,并且在处理完成之后删除锁 doSomething(); //重新注册一把新的锁 thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } } } catch (Exception e) { e.printStackTrace(); } } }); // 1、程序一进来就先注册一把锁到zk上 thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); // wait一小会,便于观察 Thread.sleep(new Random().nextInt(1000)); // 从zk的锁父目录下,获取所有子节点,并且注册对父节点的监听 List<String> childrenNodes = zk.getChildren("/" + groupNode, true); //如果争抢资源的程序就只有自己,则可以直接去访问共享资源 if (childrenNodes.size() == 1) { doSomething(); thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } } /** * 处理业务逻辑,并且在最后释放锁 */ private void doSomething() throws Exception { try { System.out.println("gain lock: " + thisPath); Thread.sleep(2000); // do something } finally { System.out.println("finished: " + thisPath); zk.delete(this.thisPath, -1); } } public static void main(String[] args) throws Exception { DistributedClientLock dl = new DistributedClientLock(); dl.connectZookeeper(); Thread.sleep(Long.MAX_VALUE); } }