zookeeper之分布式队列

设计:

使用路径为/queue的znode下的节点表示队列中的元素。/queue下的节点都是顺序持久化的znode。这些znode名字的后缀数字表示了对应队列元素在队列中的位置。Znode名字后缀数字越小,对应队列元素在队列中的位置越靠前。

offer方法:

在/queue下面创建一个顺序znode。因为znode的后缀数字是/queue下面现有znode最大后缀数字加1,所有znode对应的队列元素处于队尾

    /**
     * 向队列提交数据
     *
     * @param element 提交的数据
     * @return
     * @throws Exception
     */
    public boolean offer(T element) throws Exception {

        String nodeFullPath = root.concat("/").concat(Node_NAME);
        try {
            // 创建持久的顺序节点
            zkClient.createPersistentSequential(nodeFullPath, element);
        } catch (ZkNoNodeException e) {
            zkClient.createPersistent(root);
            offer(element);
        } catch (Exception e) {
            throw ExceptionUtil.convertToRuntimeException(e);
        }
        return true;
    }

poll方法:

/**
     * 从队列获取数据
     *
     * @return
     * @throws Exception
     */
    public T poll() throws Exception {
        try {

            List<String> list = zkClient.getChildren(root);
            if (list.size() == 0) {
                return null;
            }

            // 排序队列 根据名称由小到大
            Collections.sort(list, new Comparator<String>() {
                public int compare(String lhs, String rhs) {
                    return getNodeNumber(lhs, Node_NAME).compareTo(getNodeNumber(rhs, Node_NAME));
                }
            });

            for (String nodeName : list) {

                String nodeFullPath = root.concat("/").concat(nodeName);
                try {
                    T node = (T) zkClient.readData(nodeFullPath);
                    zkClient.delete(nodeFullPath);
                    return node;
                } catch (ZkNoNodeException e) {
                    // 其他客户端消费了 继续循环
                }
            }
            return null;

        } catch (Exception e) {
            throw ExceptionUtil.convertToRuntimeException(e);
        }

    }

注意:T node = (T) zkClient.readData(nodeFullPath);的成功,并不代表删除的成功,原因是该队列元素可能会被其他的用户出队。

总体的代码:

package com.queue.model;


import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 7726840211952830151L;

    String name;
    String id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id='" + id + '\'' +
                '}';
    }

}
package com.queue;


import org.I0Itec.zkclient.ExceptionUtil;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNoNodeException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 简单的分布式队列
 *
 * @author jerome_s@qq.com
 * @date 2016/8/30 20:19
 */
public class DistributedSimpleQueue<T> {

    protected final ZkClient zkClient;

    /**
     * 根节点路径
     */
    protected final String root;

    /**
     * 顺序节点的前缀
     */
    protected static final String Node_NAME = "qn_";


    public DistributedSimpleQueue(ZkClient zkClient, String root) {
        this.zkClient = zkClient;
        this.root = root;
    }

    /**
     * 获取队列的大小
     *
     * @return
     */
    public int getQueueSize() {
        return zkClient.getChildren(root).size();
    }

    /**
     * 向队列提交数据
     *
     * @param element 提交的数据
     * @return
     * @throws Exception
     */
    public boolean offer(T element) throws Exception {

        String nodeFullPath = root.concat("/").concat(Node_NAME);
        try {
            // 创建持久的顺序节点
            zkClient.createPersistentSequential(nodeFullPath, element);
        } catch (ZkNoNodeException e) {
            zkClient.createPersistent(root);
            offer(element);
        } catch (Exception e) {
            throw ExceptionUtil.convertToRuntimeException(e);
        }
        return true;
    }

    public List<T> element() {
        List<String> list = zkClient.getChildren(root);
        // 排序队列 根据名称由小到大
        Collections.sort(list, new Comparator<String>() {
            public int compare(String lhs, String rhs) {
                return getNodeNumber(lhs, Node_NAME).compareTo(getNodeNumber(rhs, Node_NAME));
            }
        });
        List<T> res = new ArrayList<>();
        try {
            for (String nodeName : list) {
                String nodeFullPath = root.concat("/").concat(nodeName);
                res.add((T)zkClient.readData(nodeFullPath));
            }
        } catch (ZkNoNodeException e) {
            // 其他客户端消费了 继续循环
        }
        return res;
    }


    /**
     * 从队列获取数据
     *
     * @return
     * @throws Exception
     */
    public T poll() throws Exception {
        try {

            List<String> list = zkClient.getChildren(root);
            if (list.size() == 0) {
                return null;
            }

            // 排序队列 根据名称由小到大
            Collections.sort(list, new Comparator<String>() {
                public int compare(String lhs, String rhs) {
                    return getNodeNumber(lhs, Node_NAME).compareTo(getNodeNumber(rhs, Node_NAME));
                }
            });

            for (String nodeName : list) {

                String nodeFullPath = root.concat("/").concat(nodeName);
                try {
                    T node = (T) zkClient.readData(nodeFullPath);
                    zkClient.delete(nodeFullPath);
                    return node;
                } catch (ZkNoNodeException e) {
                    // 其他客户端消费了 继续循环
                }
            }
            return null;

        } catch (Exception e) {
            throw ExceptionUtil.convertToRuntimeException(e);
        }

    }

    private String getNodeNumber(String str, String nodeName) {
        int index = str.lastIndexOf(nodeName);
        if (index >= 0) {
            index += Node_NAME.length();
            return index <= str.length() ? str.substring(index) : "";
        }
        return str;
    }

}

测试用例:

package com.queue;

import com.queue.model.User;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;

import java.util.List;

/**
 * 测试简单的分布式队列
 *
 * @author jerome_s@qq.com
 * @date 2016/8/30 19:48
 */
public class TestDistributedSimpleQueue {

    public static void main(String[] args) {

        ZkClient zkClient = new ZkClient("192.168.40.138:2181", 10000, 500000, new SerializableSerializer());
        DistributedSimpleQueue<User> queue = new DistributedSimpleQueue<>(zkClient, "/queue");

        User user1 = new User();
        user1.setId("1");
        user1.setName("jerome1");

        User user2 = new User();
        user2.setId("2");
        user2.setName("jerome2");

        try {
            queue.offer(user1);
            queue.offer(user2);
            System.out.println("queue.offer end!");

            List<User> element = queue.element();
            System.out.println(element.toString());

            User u1 = queue.poll();
            User u2 = queue.poll();
            System.out.println("queue.poll() u1 = " + u1.toString());
            System.out.println("queue.poll() u2 = " + u2.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /* console:
    queue.offer end!
    queue.poll() u1 = User{name='jerome1', id='1'}
    queue.poll() u2 = User{name='jerome2', id='2'}
    */
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值