原理:ZooKeeper通过一个Node来维护Queue的实体,用其children来存储Queue的内容,并且ZooKeeper的create方法中提供了顺序递增的模式,会自动地在name后面加上一个递增的数字来插入新元素。可以用其children来构建一个queue的数据结构,offer的时候使用create,take的时候按照children的顺序删除第一个即可。ZooKeeper保障了各个server上数据是一致的,因此也就实现了一个分布式Queue。代码如下:
package com.zero.zookeeper;
import java.io.IOException;
import java.util.List;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class DistributedQueue {
private static DistributedQueue instance = new DistributedQueue();
private static ZooKeeper zookeeper;
private String dir = "/distributedQueue";
private String prefix = "queueIndex";
private DistributedQueue() {
if (null != instance) {
throw new AssertionError();
}
try {
zookeeper = new ZooKeeper("localhost:2181", 10000, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public static DistributedQueue getInstanceQueue() {
return instance;
}
public byte[] take() throws KeeperException, InterruptedException {
return take(Long.MAX_VALUE);
}
public byte[] take(long time) throws KeeperException, InterruptedException {
while (true) {
LatchChildWatcher childWatcher = new LatchChildWatcher();
TreeMap<Long, String> orderedChildren;
try {
orderedChildren = (TreeMap<Long, String>) orderChildren(childWatcher);
} catch (KeeperException.NoNodeException e) {
zookeeper.create(dir, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
continue;
}
if (orderedChildren.size() == 0) {
// 等待时间超时,则返回 false,否则返回 true
boolean idNotified = childWatcher.await(time);
if (idNotified) {
continue;
}
return null;
}
for (String childNode : orderedChildren.values()) {
try {
byte[] data = zookeeper.getData(childNode, false, null);
zookeeper.delete(childNode, -1);// 也会触发NodeChildrenChanged
return data;
} catch (KeeperException.NoNodeException e) {
}
}
}
}
/**
* 将数据插入队列中
*
* @param data
* @return
* @throws KeeperException
* @throws InterruptedException
*/
public boolean offer(byte[] data) throws KeeperException,
InterruptedException {
for (;;) {
try {
zookeeper.create(dir + "/" + prefix, data, Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT_SEQUENTIAL);
return true;
} catch (KeeperException.NoNodeException e) {
zookeeper.create(dir, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
}
}
public static void close() {
try {
zookeeper.close();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private TreeMap<Long, String> orderChildren(Watcher watcher)
throws NoNodeException {
Stat stat = null;
try {
stat = zookeeper.exists(dir, true);// 这里如果加watcher,会监控dir的NodeCreated与NodeDeleted
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
if (stat == null) {
throw new KeeperException.NoNodeException();
}
List<String> childernList = null;
try {
childernList = zookeeper.getChildren(dir, watcher);// 这里如果加watcher,会监控dir的NodeChildrenChanged
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
TreeMap<Long, String> orderedChildren = new TreeMap<Long, String>();
for (String childNode : childernList) {
try {
String childNodePath = dir + "/" + childNode;
Stat s = zookeeper.exists(childNodePath, true);
if (s != null) {// 多线程下可能zookeeper.getChildren后childNode被删除
orderedChildren.put(s.getCzxid(), childNodePath);
}
} catch (KeeperException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return orderedChildren;
}
/**
* Watcher是一次性,只会触发一次process,除非多次设置zookeeper.getChildren(dir, watcher)
*
* @author zero
*
*/
private class LatchChildWatcher implements Watcher {
private volatile boolean isNotify = false;
@Override
public void process(WatchedEvent event) {
if (EventType.NodeChildrenChanged == event.getType()) {
isNotify = true;
}
}
public boolean await(long time) throws InterruptedException {
long end = System.currentTimeMillis() + time * 1000;
for (;;) {
TimeUnit.MILLISECONDS.sleep(100);
if (isNotify) {
return true;
} else if (System.currentTimeMillis() >= end) {
return false;
}
}
}
}
}