引言

在这个指南中,使用展示了使用ZooKeeper实现的屏障和生产-消费队列。我们分别称这些类为Barrier和Queue。这些例子假定你至少有一个运行的ZooKeeper服务。

两个原语都使用下面的代码片断:

static ZooKeeper zk = null;
static Integer mutex; String root; SyncPrimitive(String address) {
if(zk == null){
try {
System.out.println("Starting ZK:");
zk = new ZooKeeper(address, 3000, this);
mutex = new Integer(-1);
System.out.println("Finished starting ZK: " + zk);
} catch (IOException e) {
System.out.println(e.toString());
zk = null;
}
}
} synchronized public void process(WatchedEvent event) {
synchronized (mutex) {
mutex.notify();
}
}

两个类都扩展了SyncPrimitive。用这种方式,我们的执行步骤和SyncPrimitive的构造函数的所有原语差不多。为了保持例子简单,我们创建一个ZooKeeper对象,在我们每一次实例化barrier对象或queue对象的时候,并且我们声明一个静态的变量引用这个对象。随后的Barrier对象和Queue检查一个ZooKeeper对象是否存在。另外,我们可以有一个创建ZooKeeper的应用并且传递它给Barrier和Queue的构造函数。

我们使用process()方法来处理监视器的通知。在下面的讨论,我们呈现设置监视器的代码。一个监视器是一个内部的数据结构,它能够使ZooKeeper通知节点的改变。例如,如果一个客户端正在 等待其它客户端离开一个屏障,那么它可以给一个特定的节点设置一个监视器并且等待修改。这能表示它等待结束了。一旦你看完例子你就明白这一点。

屏障

一个屏障是一个原语,它使一组进程可以同步地开始和结束一个计算。这种实现的总体思想是有一个barrier节点作为每一个进程节点的父节点。假如我们这个屏障节点为"/b1"。每一个进程"p"创建一个节点”/b1/p“。一旦有足够的进程已经创建的它们对象的节点,加入的进程可以开始计算。

在这个例子中,每一个进程代表一个Barrier对象,并且它的构造函数有这些参数:

  • ZooKeeeper服务端的地址(例如"zoo1.foo.com:2181")
  • ZooKeeper中屏障节点的路径(例如,"/b1")
  • 这组进程的数量

Barrier的构造函数传递ZooKeeper服务端的地址给父类的构造器。父类创建一个ZooKeeper实例如果没有这个实例。Barrier的构造函数在ZooKeeper上创建一个节点,这个节点是所有进程节点的父节点,并且 我们称它为root(注意:它不是ZooKeeper的根"/").

 /**
* Barrier constructor
*
* @param address
* @param root
* @param size
*/
Barrier(String address, String root, int size) {
super(address);
this.root = root;
this.size = size; // Create barrier node
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
System.out
.println("Keeper exception when instantiating queue: "
+ e.toString());
} catch (InterruptedException e) {
System.out.println("Interrupted exception");
}
} // My node name
try {
name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString());
} catch (UnknownHostException e) {
System.out.println(e.toString());
} }

为了进入屏障,一个进程调用enter()。一个进程在root下面创建一个节来代表它,用它的主机名来表示节点的名字。它然后等待直到足够的进程进入到屏障。一个进程通过用getChildren()检查root节点的孩子节点数量来实现。并且等待通知一旦它没有足够的孩子节点。为了收到一个通知当root节点改变的时候,一个进程不得不设置 一个监视器,并且通过调用"getChildren()"来实现。在代码中,我们的"getChildren()"方法有两个参数。第一个是那一个节点来读数据的,并且第二个是布尔标识使进程设置 一个监视器,在代码中标识是true.

 /**
* Join barrier
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/ boolean enter() throws KeeperException, InterruptedException{
zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
while (true) {
synchronized (mutex) {
List<String> list = zk.getChildren(root, true); if (list.size() < size) {
mutex.wait();
} else {
return true;
}
}
}
}

注意enter()抛出KeeperException 和InterruptedException异常,所以应用有责任来捕获和处理这样的异常。

一旦计算结束,一个进程调用leave()来离开这个屏障。首先它删除它对应的节点,然后它得到root节点的孩子。如果至少有一个孩子,那么它就会等待一个通知(注意调用getChildred()的第二个参数是true),意为着ZooKeeper不得不设置一个监视器在root节点上。一旦收到一个通知,它再次检查是否root节点有任何孩子。

        /**
* Wait until all reach barrier
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/ boolean leave() throws KeeperException, InterruptedException{
zk.delete(root + "/" + name, 0);
while (true) {
synchronized (mutex) {
List<String> list = zk.getChildren(root, true);
if (list.size() > 0) {
mutex.wait();
} else {
return true;
}
}
}
}
}

生产者-消费者队列

一个生产者-消费者队列是一个数据分发结构,一组进程生产并且消费物品。生产者进程创建新的元素并且加入到队列。消费者进程从队列中删除元素,并且处理它们。在这个实现中,元素中简单的数字。队列被一个root节点表示,并且加入一个元素到这个队列,一个生产者进程创建一个新节点,root节点的孩子节点。

下面的代码片断对象对象的构造函数。就像Barrier对象一样,它首先父类的构造函数,SyncPrimitive,来创建一个ZooKeeper对象,如果这个对象不存在。它然后校验队列的root节点是否存在,并且如果不存在就创建一个。

/**
* Constructor of producer-consumer queue
*
* @param address
* @param name
*/
Queue(String address, String name) {
super(address);
this.root = name;
// Create ZK node name
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
System.out
.println("Keeper exception when instantiating queue: "
+ e.toString());
} catch (InterruptedException e) {
System.out.println("Interrupted exception");
}
}
}

一个生产者进程调用 "produce()" 来增加元素到队列中,并且传一个数字作为参数。为了增加元素到队列中,这个方法创建一个节点通过"create()”,并且使用SEQUENCE标示来让ZooKeeper增加序列计数器的值到root节点上。用这种方法,我们暴露了队列中元素的总的顺序,因此保证队列中最老的元素是下一个要消费的元素。

  /**
* Add element to the queue.
*
* @param i
* @return
*/ boolean produce(int i) throws KeeperException, InterruptedException{
ByteBuffer b = ByteBuffer.allocate(4);
byte[] value; // Add child with value i
b.putInt(i);
value = b.array();
zk.create(root + "/element", value, Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT_SEQUENTIAL); return true;
}

为了消费一个元素,一个消费者进程获取root节点的孩子,读取最小值的节点,并且返回这个元素。注意如果有一个冲突,那么两个竞争进程中的一个将不能删除它的节点并且删除操作将会抛出异常。

调用getChildren()返回一个以字典序列排序的列表。因为字典序列对计算器的值的顺序没有必要,我们需要那一个元素是最小的值。来了决定哪一个有最小计算器值,我们遍历列表,并且删除每一个的前缀"element"。

 /**
* Remove first element from the queue.
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/
int consume() throws KeeperException, InterruptedException{
int retvalue = -1;
Stat stat = null; // Get the first element available
while (true) {
synchronized (mutex) {
List<String> list = zk.getChildren(root, true);
if (list.size() == 0) {
System.out.println("Going to wait");
mutex.wait();
} else {
Integer min = new Integer(list.get(0).substring(7));
for(String s : list){
Integer tempValue = new Integer(s.substring(7));
//System.out.println("Temporary value: " + tempValue);
if(tempValue < min) min = tempValue;
}
System.out.println("Temporary value: " + root + "/element" + min);
byte[] b = zk.getData(root + "/element" + min,
false, stat);
zk.delete(root + "/element" + min, 0);
ByteBuffer buffer = ByteBuffer.wrap(b);
retvalue = buffer.getInt(); return retvalue;
}
}
}
}
}

完整的源码列表

SyncPrimitive.Java

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random; import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat; public class SyncPrimitive implements Watcher { static ZooKeeper zk = null;
static Integer mutex; String root; SyncPrimitive(String address) {
if(zk == null){
try {
System.out.println("Starting ZK:");
zk = new ZooKeeper(address, 3000, this);
mutex = new Integer(-1);
System.out.println("Finished starting ZK: " + zk);
} catch (IOException e) {
System.out.println(e.toString());
zk = null;
}
}
//else mutex = new Integer(-1);
} synchronized public void process(WatchedEvent event) {
synchronized (mutex) {
//System.out.println("Process: " + event.getType());
mutex.notify();
}
} /**
* Barrier
*/
static public class Barrier extends SyncPrimitive {
int size;
String name; /**
* Barrier constructor
*
* @param address
* @param root
* @param size
*/
Barrier(String address, String root, int size) {
super(address);
this.root = root;
this.size = size; // Create barrier node
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
System.out
.println("Keeper exception when instantiating queue: "
+ e.toString());
} catch (InterruptedException e) {
System.out.println("Interrupted exception");
}
} // My node name
try {
name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString());
} catch (UnknownHostException e) {
System.out.println(e.toString());
} } /**
* Join barrier
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/ boolean enter() throws KeeperException, InterruptedException{
zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
while (true) {
synchronized (mutex) {
List<String> list = zk.getChildren(root, true); if (list.size() < size) {
mutex.wait();
} else {
return true;
}
}
}
} /**
* Wait until all reach barrier
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/ boolean leave() throws KeeperException, InterruptedException{
zk.delete(root + "/" + name, 0);
while (true) {
synchronized (mutex) {
List<String> list = zk.getChildren(root, true);
if (list.size() > 0) {
mutex.wait();
} else {
return true;
}
}
}
}
} /**
* Producer-Consumer queue
*/
static public class Queue extends SyncPrimitive { /**
* Constructor of producer-consumer queue
*
* @param address
* @param name
*/
Queue(String address, String name) {
super(address);
this.root = name;
// Create ZK node name
if (zk != null) {
try {
Stat s = zk.exists(root, false);
if (s == null) {
zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
} catch (KeeperException e) {
System.out
.println("Keeper exception when instantiating queue: "
+ e.toString());
} catch (InterruptedException e) {
System.out.println("Interrupted exception");
}
}
} /**
* Add element to the queue.
*
* @param i
* @return
*/ boolean produce(int i) throws KeeperException, InterruptedException{
ByteBuffer b = ByteBuffer.allocate(4);
byte[] value; // Add child with value i
b.putInt(i);
value = b.array();
zk.create(root + "/element", value, Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT_SEQUENTIAL); return true;
} /**
* Remove first element from the queue.
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/
int consume() throws KeeperException, InterruptedException{
int retvalue = -1;
Stat stat = null; // Get the first element available
while (true) {
synchronized (mutex) {
List<String> list = zk.getChildren(root, true);
if (list.size() == 0) {
System.out.println("Going to wait");
mutex.wait();
} else {
Integer min = new Integer(list.get(0).substring(7));
for(String s : list){
Integer tempValue = new Integer(s.substring(7));
//System.out.println("Temporary value: " + tempValue);
if(tempValue < min) min = tempValue;
}
System.out.println("Temporary value: " + root + "/element" + min);
byte[] b = zk.getData(root + "/element" + min,
false, stat);
zk.delete(root + "/element" + min, 0);
ByteBuffer buffer = ByteBuffer.wrap(b);
retvalue = buffer.getInt(); return retvalue;
}
}
}
}
} public static void main(String args[]) {
if (args[0].equals("qTest"))
queueTest(args);
else
barrierTest(args); } public static void queueTest(String args[]) {
Queue q = new Queue(args[1], "/app1"); System.out.println("Input: " + args[1]);
int i;
Integer max = new Integer(args[2]); if (args[3].equals("p")) {
System.out.println("Producer");
for (i = 0; i < max; i++)
try{
q.produce(10 + i);
} catch (KeeperException e){ } catch (InterruptedException e){ }
} else {
System.out.println("Consumer"); for (i = 0; i < max; i++) {
try{
int r = q.consume();
System.out.println("Item: " + r);
} catch (KeeperException e){
i--;
} catch (InterruptedException e){ }
}
}
} public static void barrierTest(String args[]) {
Barrier b = new Barrier(args[1], "/b1", new Integer(args[2]));
try{
boolean flag = b.enter();
System.out.println("Entered barrier: " + args[2]);
if(!flag) System.out.println("Error when entering the barrier");
} catch (KeeperException e){ } catch (InterruptedException e){ } // Generate random integer
Random rand = new Random();
int r = rand.nextInt(100);
// Loop for rand iterations
for (int i = 0; i < r; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
try{
b.leave();
} catch (KeeperException e){ } catch (InterruptedException e){ }
System.out.println("Left barrier");
}
}

ZooKeeper屏障和队列的指南(七)的更多相关文章

  1. zookeeper应用:屏障、队列、分布式锁

    zookeeper工具类: 获取连接实例:创建节点:获取子节点:设置节点数据:获取节点数据:访问控制等. package org.windwant.zookeeper; import org.apac ...

  2. Swift语言指南(七)--语言基础之布尔值和类型别名

    原文:Swift语言指南(七)--语言基础之布尔值和类型别名 布尔值 Swift有一个基本布尔类型,叫做布尔(bool),布尔值又称逻辑值(logical),因为它只能为真(true)或假(false ...

  3. ZooKeeper实现分布式队列Queue

    ZooKeeper实现分布式队列Queue 让Hadoop跑在云端系列文章,介绍了如何整合虚拟化和Hadoop,让Hadoop集群跑在VPS虚拟主机上,通过云向用户提供存储和计算的服务. 现在硬件越来 ...

  4. Solr集群的搭建以及使用(内涵zookeeper集群的搭建指南)

    1   什么是SolrCloud SolrCloud(solr 云)是Solr提供的分布式搜索方案,当你需要大规模,容错,分布式索引和检索能力时使用 SolrCloud.当一个系统的索引数据量少的时候 ...

  5. [转载] ZooKeeper实现分布式队列Queue

    转载自http://blog.fens.me/zookeeper-queue/ 让Hadoop跑在云端系列文章,介绍了如何整合虚拟化和Hadoop,让Hadoop集群跑在VPS虚拟主机上,通过云向用户 ...

  6. ZooKeeper 实现分布式队列

    使用场景  在传统的单进程编程中,我们使用队列来存储数据结构,用来在多线程之间共享或者传递数据.在分布式环境下,同样需要一个类似单进程的组件, 用来实现跨进程.跨主机.跨网络的数据共享和数据传递.这就 ...

  7. Zookeeper应用之——队列(Queue)

    为了在Zookeeper中实现分布式队列,首先需要设计一个znode来存放数据,这个节点叫做队列节点,我们的例子中这个节点是/zookeeper/queue. 生产者向队列中存放数据,每一个消息都是队 ...

  8. kafak manager + zookeeper + kafka 消费队列快速清除

    做性能测试时,kafka消息队列比较长,让程序自己消费完毕需要等待很长时间.就需要快速清理kafka队列 清理方式把 这kafak manager + zookeeper + kafka 这些应用情况 ...

  9. 一种基于zookeeper的分布式队列的设计与实现

    package com.ysl.zkclient.queue; import com.ysl.zkclient.ZKClient; import com.ysl.zkclient.exception. ...

随机推荐

  1. pxe+kickstart无人值守安装

    常用软件安装及使用目录 第1章 以前是怎么安装系统的 l 光盘(ISO文件,光盘的镜像文件)===>每一台物理机都得给一个光驱,如果用外置光驱的话,是不是每台机器都需要插一下 l U盘:ISO镜 ...

  2. 【转】CentOS: 开放80、22、3306端口操作

    #/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT#/sbin/iptables -I INPUT -p tcp --dport 22 -j AC ...

  3. 展示github中的页面(Github Pages)

    一.创建一个仓库,名为"user_name.github.io"(此处user_name替换为你自己的github用户名),并在根目录下创建index.html,则该仓库下的所有h ...

  4. win7 64位在线编辑dsoframer控件的安装和使用配置

    经历了两天的折磨,查阅了网上的资料,按网上的操作试了n种方法结果还是不行,开始以为是dsoframer 是32位控件问题,结果不是(经历了更改解决方案cpu,发布基于x86的网站:以为是操作系统问题, ...

  5. YaoLingJump开发者日志(七)

      LGame用起来真是各种蛋疼,插背景都可以显示不出来.在屏幕结束后释放资源,重载该屏幕时再setbackground也不行,直接用Lpaper当background更不行,会把tilemap上的东 ...

  6. vue服务端渲染简单入门实例

    想到要学习vue-ssr的同学,自不必多说,一定是熟悉了vue,并且多多少少做过几个项目.然后学习vue服务端渲染无非解决首屏渲染的白屏问题以及SEO友好. 话不多说,笔者也是研究多日才搞明白这个服务 ...

  7. PowerMock用法[转]

    转:http://agiledon.github.io/blog/2013/11/21/play-trick-with-powermock/ 当我们面对一个遗留系统时,常见的问题是没有测试.正如Mic ...

  8. 第二部分shell编程2正则(grepegrepsedawk)

    一.grep/egrep 1. 语法+选项语法: grep [-cinvABC] 'word' filename -c :打印符合要求的行数-n :在输出符合要求的行的同时连同行号一起输出 -v :打 ...

  9. MyBatis配置和日志

    MyBatis最关键的组成部分是SqlSessionFactory,我们可以从中获取SqlSession,并执行映射的SQL语句.SqlSessionFactory对象可以通过基于XML的配置信息或者 ...

  10. OBJ文件

    OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模 ...