Curator也提供ZK Recipe的分布式队列实现。利用ZK的 PERSISTENTSEQUENTIAL节点,可以保证放入到队列中的项目是按照顺序排队的。如果单一的消费者从队列中取数据,那么它是先入先出的,这也是队列的特点。如果你严格要求顺序,你就得使用单一的消费者,可以使用leader选举只让leader作为唯一的消费者。但是,根据Netflix的Curator作者所说,ZooKeeper真心不适合做Queue,或者说ZK没有实现一个好的Queue,详细内容可以看 Tech Note 4,原因有五:
  • ZK有1MB 的传输限制。实践中ZNode必须相对较小,而队列包含成千上万的消息,非常的大
  • 如果有很多节点,ZK启动时相当的慢。而使用queue会导致好多ZNode。你需要显著增大 initLimit 和 syncLimit
  • ZNode很大的时候很难清理。Netflix不得不创建了一个专门的程序做这事
  • 当很大量的包含成千上万的子节点的ZNode时,ZK的性能变得不好
  • ZK的数据库完全放在内存中。大量的Queue意味着会占用很多的内存空间
尽管如此,Curator还是创建了各种Queue的实现。如果Queue的数据量不太多,数据量不太大的情况下,酌情考虑,还是可以使用的。

1.DistributedQueue

1. DistributedQueue介绍
DistributedQueue是最普通的一种队列。 它设计以下四个类:
  • QueueBuilder - 创建队列使用QueueBuilder,它也是其它队列的创建类
  • QueueConsumer - 队列中的消息消费者接口
  • QueueSerializer - 队列消息序列化和反序列化接口,提供了对队列中的对象的序列化和反序列化
  • DistributedQueue - 队列实现类
    QueueConsumer是消费者,它可以接收队列的数据。处理队列中的数据的代码逻辑可以放在QueueConsumer.consumeMessage()中。
    正常情况下先将消息从队列中移除,再交给消费者消费。但这是两个步骤,不是原子的。可以调用Builder的lockPath()消费者加锁,当消费者消费数据时持有锁,这样其它消费者不能消费此消息。如果消费失败或者进程死掉,消息可以交给其它进程。这会带来一点性能的损失。最好还是单消费者模式使用队列。
2.编写示例程序
  1. public class DistributedQueueExample
  2. {
  3. private static final String PATH = "/example/queue";
  4. public static void main(String[] args) throws Exception
  5. {
  6. CuratorFramework clientA = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
  7. clientA.start();
  8. CuratorFramework clientB = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
  9. clientB.start();
  10. DistributedQueue<String> queueA = null;
  11. QueueBuilder<String> builderA = QueueBuilder.builder(clientA, createQueueConsumer("A"), createQueueSerializer(), PATH);
  12. queueA = builderA.buildQueue();
  13. queueA.start();
  14. DistributedQueue<String> queueB = null;
  15. QueueBuilder<String> builderB = QueueBuilder.builder(clientB, createQueueConsumer("B"), createQueueSerializer(), PATH);
  16. queueB = builderB.buildQueue();
  17. queueB.start();
  18. for (int i = 0; i < 100; i++)
  19. {
  20. queueA.put(" test-A-" + i);
  21. Thread.sleep(10);
  22. queueB.put(" test-B-" + i);
  23. }
  24. Thread.sleep(1000 * 10);// 等待消息消费完成
  25. queueB.close();
  26. queueA.close();
  27. clientB.close();
  28. clientA.close();
  29. System.out.println("OK!");
  30. }
  31. /** 队列消息序列化实现类 */
  32. private static QueueSerializer<String> createQueueSerializer()
  33. {
  34. return new QueueSerializer<String>()
  35. {
  36. @Override
  37. public byte[] serialize(String item)
  38. {
  39. return item.getBytes();
  40. }
  41. @Override
  42. public String deserialize(byte[] bytes)
  43. {
  44. return new String(bytes);
  45. }
  46. };
  47. }
  48. /** 定义队列消费者 */
  49. private static QueueConsumer<String> createQueueConsumer(final String name)
  50. {
  51. return new QueueConsumer<String>()
  52. {
  53. @Override
  54. public void stateChanged(CuratorFramework client, ConnectionState newState)
  55. {
  56. System.out.println("连接状态改变: " + newState.name());
  57. }
  58. @Override
  59. public void consumeMessage(String message) throws Exception
  60. {
  61. System.out.println("消费消息(" + name + "): " + message);
  62. }
  63. };
  64. }
  65. }
以上程序创建两个client(A和B),它们在同一路径上创建队列(A和B),同时发消息、同时消费消息。
3.示例程序运行结果
    运行结果控制台(观察ClientA可以消费ClientB的消息):
  1. 消费消息(A): test-A-0
  2. 消费消息(A): test-B-0
  3. ......
  4. 消费消息(B): test-A-51
  5. 消费消息(B): test-B-51
  6. 消费消息(B): test-A-52
  7. 消费消息(B): test-B-52
  8. 消费消息(B): test-A-53
  9. 消费消息(B): test-B-54
  10. 消费消息(B): test-A-55
  11. ......
  12. 消费消息(A): test-A-99
  13. 消费消息(A): test-B-99
  14. OK!
    Zookeeper节点信息如下:

2.DistributedIdQueue

    DistributedIdQueue和上面的队列类似,但是可以为队列中的每一个元素设置一个ID。可以通过ID把队列中任意的元素移除。
1.DistributedIdQueue结束
DistributedIdQueue的使用与上面队列的区别是:
  • 通过下面方法创建:builder.buildIdQueue()
  • 放入元素时:queue.put(aMessage, messageId);
  • 移除元素时:int numberRemoved = queue.remove(messageId);
2.编写示例程序
在这个例子中,有些元素还没有被消费者消费时就移除了,这样消费者不会收到删除的消息。(此示例是根据上面例子修改而来)
  1. public class DistributedIdQueueExample
  2. {
  3. private static final String PATH = "/example/queue";
  4. public static void main(String[] args) throws Exception
  5. {
  6. CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
  7. client.start();
  8. DistributedIdQueue<String> queue = null;
  9. QueueConsumer<String> consumer = createQueueConsumer("A");
  10. QueueBuilder<String> builder = QueueBuilder.builder(client, consumer, createQueueSerializer(), PATH);
  11. queue = builder.buildIdQueue();
  12. queue.start();
  13. for (int i = 0; i < 10; i++)
  14. {
  15. queue.put(" test-" + i, "Id" + i);
  16. Thread.sleep((long) (50 * Math.random()));
  17. queue.remove("Id" + i);
  18. }
  19. Thread.sleep(1000 * 3);
  20. queue.close();
  21. client.close();
  22. System.out.println("OK!");
  23. }
  24. ......
  25. }
3.示例程序运行结果
    运行结果控制台:
  1. 消费消息(A): test-2
  2. 消费消息(A): test-3
  3. 消费消息(A): test-4
  4. 消费消息(A): test-7
  5. OK!

3.DistributedPriorityQueue

    优先级队列对队列中的元素按照优先级进行排序。Priority越小,元素月靠前,越先被消费掉。
1.DistributedPriorityQueue介绍
    通过builder.buildPriorityQueue(minItemsBeforeRefresh)方法创建。
    当优先级队列得到元素增删消息时,它会暂停处理当前的元素队列,然后刷新队列。minItemsBeforeRefresh指定刷新前当前活动的队列的最小数量。主要设置你的程序可以容忍的不排序的最小值。
    放入队列时需要指定优先级:queue.put(aMessage, priority);
2.编写示例程序
  1. public class DistributedPriorityQueueExample
  2. {
  3. private static final String PATH = "/example/queue";
  4. public static void main(String[] args) throws Exception
  5. {
  6. CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
  7. client.start();
  8. DistributedPriorityQueue<String> queue = null;
  9. QueueConsumer<String> consumer = createQueueConsumer("A");
  10. QueueBuilder<String> builder = QueueBuilder.builder(client, consumer, createQueueSerializer(), PATH);
  11. queue = builder.buildPriorityQueue(0);
  12. queue.start();
  13. for (int i = 0; i < 5; i++)
  14. {
  15. int priority = (int) (Math.random() * 100);
  16. System.out.println("test-" + i + " 优先级:" + priority);
  17. queue.put("test-" + i, priority);
  18. Thread.sleep((long) (50 * Math.random()));
  19. }
  20. Thread.sleep(1000 * 2);
  21. queue.close();
  22. client.close();
  23. }
  24. ......
  25. }
3.示例程序运行结果
    运行结果控制台:
  1. test-0 优先级:34
  2. test-1 优先级:51
  3. test-2 优先级:63
  4. test-3 优先级:45
  5. test-4 优先级:36
  6. 消费消息(A): test-0
  7. 消费消息(A): test-4
  8. 消费消息(A): test-3
  9. 消费消息(A): test-1
  10. 消费消息(A): test-2
  11. OK!

4.DistributedDelayQueue

    JDK中也有DelayQueue,不知道你是否熟悉。DistributedDelayQueue也提供了类似的功能,元素有个delay值,消费者隔一段时间才能收到元素。
1. DistributedDelayQueue介绍
放入元素时可以指定delayUntilEpoch:queue.put(aMessage, delayUntilEpoch);
注意:delayUntilEpoch不是离现在的一个时间间隔,比如20毫秒,而是未来的一个时间戳,如 System.currentTimeMillis() + 10秒。如果delayUntilEpoch的时间已经过去,消息会立刻被消费者接收。
2.编写示例程序
  1. public class DistributedDelayQueueExample
  2. {
  3. private static final String PATH = "/example/queue";
  4. public static void main(String[] args) throws Exception
  5. {
  6. CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
  7. client.start();
  8. DistributedDelayQueue<String> queue = null;
  9. QueueConsumer<String> consumer = createQueueConsumer("A");
  10. QueueBuilder<String> builder = QueueBuilder.builder(client, consumer, createQueueSerializer(), PATH);
  11. queue = builder.buildDelayQueue();
  12. queue.start();
  13. for (int i = 0; i < 10; i++)
  14. {
  15. queue.put("test-" + i, System.currentTimeMillis() + 3000);
  16. }
  17. System.out.println("put 完成!");
  18. Thread.sleep(1000 * 5);
  19. queue.close();
  20. client.close();
  21. System.out.println("OK!");
  22. }
  23. ......
  24. }
3.示例程序运行结果
    运行结果控制台:
  1. put 完成!
  2. 消费消息(A): test-0
  3. 消费消息(A): test-3
  4. 消费消息(A): test-1
  5. 消费消息(A): test-2
  6. 消费消息(A): test-6
  7. 消费消息(A): test-4
  8. 消费消息(A): test-5
  9. 消费消息(A): test-7
  10. 消费消息(A): test-8
  11. 消费消息(A): test-9
  12. OK!

5.SimpleDistributedQueue

    前面虽然实现了各种队列,但是你注意到没有,这些队列并没有实现类似JDK一样的接口。SimpleDistributedQueue提供了和JDK一致性的接口(但是没有实现Queue接口)。
1. SimpleDistributedQueue介绍
SimpleDistributedQueue常用方法:
  1. // 创建
  2. public SimpleDistributedQueue(CuratorFramework client, String path)
  3. // 增加元素
  4. public boolean offer(byte[] data) throws Exception
  5. // 删除元素
  6. public byte[] take() throws Exception
  7. // 另外还提供了其它方法
  8. public byte[] peek() throws Exception
  9. public byte[] poll(long timeout, TimeUnit unit) throws Exception
  10. public byte[] poll() throws Exception
  11. public byte[] remove() throws Exception
  12. public byte[] element() throws Exception
没有add方法,多了take方法。take方法在成功返回之前会被阻塞。而poll在队列为空时直接返回null。

-------------------------------------------------------------------------------------------------------------------------------

10.Curator队列的更多相关文章

  1. 10 阻塞队列 & 生产者-消费者模式

    原文:http://www.cnblogs.com/dolphin0520/p/3932906.html 在前面我们接触的队列都是非阻塞队列,比如PriorityQueue.LinkedList(Li ...

  2. java数据结构-10循环队列

    一.概念: 循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用 二.代码实现: @SuppressWarnings("unchecked" ...

  3. JUC 并发编程--10, 阻塞队列之--LinkedBlockingDeque 工作窃取, 代码演示

    直接上代码 class LinkedBlockingDequeDemo { // 循环是否结束的开关 private static volatile boolean flag1 = true; pri ...

  4. java多线程系列10 阻塞队列模拟

    接下来的几篇博客会介绍下juc包下的相关数据结构 包含queue,list,map等 这篇文章主要模拟下阻塞队列. 下面是代码 import java.util.LinkedList; import ...

  5. Nodejs事件引擎libuv源码剖析之:高效队列(queue)的实现

     声明:本文为原创博文,转载请注明出处. 在libuv中,有一个只使用简单的宏封装成的高效队列(queue),现在我们就来看一下它是怎么实现的. 首先,看一下queue中最基本的几个宏: typede ...

  6. javascript数据结构与算法---队列

    javascript数据结构与算法---队列 队列是一种列表,不同的是队列只能在队尾插入元素,在队首删除元素.队列用于存储按顺序排列的数据,先进先出,这点和栈不一样(后入先出).在栈中,最后入栈的元素 ...

  7. Java数据结构之队列的实现以及队列的应用之----简单生产者消费者应用

    Java数据结构之---Queue队列 队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在 ...

  8. java中使用队列:java.util.Queue (转)

    Queue接口与List.Set同一级别,都是继承了Collection接口.LinkedList实现了Queue接 口.Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类 ...

  9. java 队列基础操作

    http://www.cnblogs.com/fuck1/p/5996116.html 队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别 ...

随机推荐

  1. 关于Cocos2d-x的瓦片地图

    1.cocos2d-x的瓦片地图是用Tiled地图编辑器做的,这个软件开源,免费,一般都是用它制作瓦片地图. 2.瓦片地图是由块层和对象组成的,块层的作用是显示和一些重叠的时候覆盖角色的作用,而对象是 ...

  2. e645. 处理键盘事件

    You can get the key that was pressed either as a key character (which is a Unicode character) or as ...

  3. 标准的 C++ 由三个重要部分组成

    标准的 C++ 由三个重要部分组成: 核心语言,提供了所有构件块,包括变量.数据类型和常量,等等.C++ 标准库,提供了大量的函数,用于操作文件.字符串等.标准模板库(STL),提供了大量的方法,用于 ...

  4. java---EL与ONGL的区别

    EL表达式: >>单纯在jsp页面中出现,是在四个作用域中取值,page,request,session,application.>>如果在struts环境中,它除了有在上面的 ...

  5. C/C++,从未过时的编程语言之父

    C/C++,持续火爆的编程语言之父 --訪传智播客C/C++学院院长传智·萧峰 编程语言作为实现互联网+基础必备工具,构建着互联网行业美轮美奂的大时代.作为编程语言之父--C语言,更是如鱼得水,在甘愿 ...

  6. react新手入门(序)

    之前在软件园使用的是react,当时为了做个集光推送,自己去搭过react,这次项目中继续使用react,于是又重新操作了遍,恰巧公司买了本react的书籍,这本书写的非常好,看着并不觉得拗口,很容易 ...

  7. Oracle查询优化--排序

    --普通排序 SELECT * FROM emp ORDER BY sal DESC; --使用列序排序 DESC; --组合排序 DESC; --translate函数,参数分别用A.B.C表示 S ...

  8. ms-SQL 递归调用

    ----递归函数-------------------------------------------------------------------------- create function d ...

  9. plsql developer中,清除登录历史

    需求描述: 在使用plsql developer的时候,发现登录的时候,有太多的历史,想要把这些登录历史清除掉, 在此记录下. 操作过程: 1.登录plsql developer(或者在登录时取消也会 ...

  10. oracle中获取执行计划

    1. 预估执行计划 - Explain PlanExplain plan以SQL语句作为输入,得到这条SQL语句的执行计划,并将执行计划输出存储到计划表中. 首先,在你要执行的SQL语句前加expla ...