zookeeper实现分布式队列
利用zookeeper的有序节点和节点一旦被删除,其他连接就不能重复删除的特性实现一个非常简单的分布式队列,说实话,我觉得用zookeeper实习分布式队列并不合适,而且我这里实现的也是最简单的,效率也不高
实现细节,利用create -s /queue/q_创建有序节点,这样数据就放到队列了,当然这一步是使用zookeeper的客户端Curator实现的,然后取出数据是,查看/queue节点下的所有子节点,拿到序号最小的那个,然后取出这个节点中数据,最后删除这个节点,但是这个时候我们可能删除不成功,因为这里是一个分布式环境,在当前机器删除节点之前,很有可能其他机器已经删除了这个节点,这个时候就需要重写拿取新的数据,下边看代码
注意,部分代码实现看[[zookeeper实现分布式锁]](https://blog.csdn.net/mjlfto/article/details/104063884)
/**
* @ClassName ZkDisQueue
* @Author mjlft
* @Date 2020/1/22 10:00
* @Version 1.0
* @Description zookeeper分布式队列
*/
public interface ZkDisQueue {
void offer(Object object) throws Exception;
Object poll() throws Exception;
}
具体实现:
/**
* @ClassName ZkDisQueueImpl
* @Author mjlft
* @Date 2020/1/22 10:02
* @Version 1.0
* @Description TODO
*/
public class ZkDisQueueImpl extends Service implements ZkDisQueue {
private Integer size;
//存放数据的有序节点前缀
private final static String QUEUE_PATH = "/queue/q_";
private class Data {
private Object current = null;
public Object getCurrent() {
return current;
}
public void setCurrent(Object current) {
this.current = current;
}
}
private Data data = new Data();
private CountDownLatch countDownLatch = new CountDownLatch(1);
public ZkDisQueueImpl(CuratorFramework client, int size) {
super.client = client;
this.size = size;
}
@Override
public void offer(Object object) throws Exception {
if (client.checkExists().forPath("/queue") == null) {
client.create().forPath("/queue");
}
List<String> children = client.getChildren().forPath("/queue");
if (children.isEmpty() || children.size() < this.size) {
client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(QUEUE_PATH);
}else {
throw new Exception("队列满了");
}
}
@Override
public Object poll() throws Exception {
//当前poll节点不存在
boolean gotResult = false;
Object result = null;
while (!gotResult) {
List<String> children = client.getChildren().forPath("/queue");
if (children == null || children.isEmpty()) {
return "队列空了";
}
children.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
result = children.get(0);
try {
client.delete().forPath("/queue/" + result);
gotResult = true;
} catch (Exception e) {
// e.printStackTrace();
}
}
return result;
}
}
测试代码:
/**
* @ClassName Test
* @Author mjlft
* @Date 2020/1/22 12:36
* @Version 1.0
* @Description TODO
*/
public class Test {
public static void main(String[] args) {
RetryPolicy retryPolicy = new RetryNTimes(3, 100);
ZkDisQueueImpl[] zkDisQueues = new ZkDisQueueImpl[3];
for (int i = 0; i < 3; i++) {
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.1.107:2181, 192.168.1.107:2182",
30 * 60 * 1000, 5 * 1000, retryPolicy);
ZkDisQueueImpl ZkDisQueueImpl = new ZkDisQueueImpl(client, 3);
ZkDisQueueImpl.start();
zkDisQueues[i] = ZkDisQueueImpl;
}
List<Thread> threads = new ArrayList<>();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.currentThread().sleep(400);
zkDisQueues[0].offer(UUID.randomUUID().toString());
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
});
for(int i = 1; i < 3; i++){
final int index = i;
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.currentThread().sleep(1000);
System.out.println(Thread.currentThread().getName() + "拿到数据:" + zkDisQueues[index].poll());
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
});
thread1.start();
}
thread.setName("t-" + 0);
thread.start();
try{
thread.join();
}catch (Exception e){
e.printStackTrace();
}
}
}
测试结果:
Thread-3拿到数据:q_0000002134
Thread-2拿到数据:q_0000002135
Thread-3拿到数据:q_0000002136
Thread-2拿到数据:q_0000002137
队列满了
Thread-3拿到数据:q_0000002138
Thread-2拿到数据:q_0000002139
Thread-3拿到数据:q_0000002140
Thread-2拿到数据:q_0000002141
队列满了
Thread-3拿到数据:q_0000002142
Thread-2拿到数据:q_0000002143
Thread-3拿到数据:q_0000002144
Thread-2拿到数据:q_0000002145
队列满了