原理http://cailin.iteye.com/blog/2014486

其实原理简单来说,就是要选举leader,会生成一个zxid,然后分发给所有的server(所以这里一台server可以接受多台server给他发送要选举leader的请求),然后各个server根据发送给自己的zxid,选择一个值最大的,然后将这个选择返回给发送这个zxid的server,只要这个server收到的答复大于等于2/n+1个(也就是超过半数的同意票),则表明自己当选为leader,然后会向所有server广播自己已经成为leader。

概述

  Zookeeper是Hadoop的一个子项目,它是分布式系统中的协调系统,可提供的服务主要有:配置服务、名字服务、分布式同步、组服务等。

它有如下的一些特点:

  • 简单

  Zookeeper的核心是一个精简的文件系统,它支持一些简单的操作和一些抽象操作,例如,排序和通知。

  • 丰富

Zookeeper的原语操作是很丰富的,可实现一些协调数据结构和协议。例如,分布式队列、分布式锁和一组同级别节点中的“领导者选举”。

  • 高可靠

  Zookeeper支持集群模式,可以很容易的解决单点故障问题。

  • 松耦合交互

    不同进程间的交互不需要了解彼此,甚至可以不必同时存在,某进程在zookeeper中留下消息后,该进程结束后其它进程还可以读这条消息。

  • 资源库

  Zookeeper实现了一个关于通用协调模式的开源共享存储库,能使开发者免于编写这类通用协议。

Getting  Started

1.独立模式安装并启动

create it in conf/zoo.cfg

  1. tickTime=2000
  2. dataDir=/var/lib/zookeeper
  3. clientPort=2181
    initLimit=10
    syncLimit=5

tickTime:指定了ZooKeeper的基本时间单位(以毫秒为单位)。

dataDir:存储内存数据快照位置。

clientPort:监听客户端连接端口。

initLimmit:启动zookeeper,从节点至主节点连接超时时间。(上面为10个tickTime)

syncLimit:zookeeper正常运行,若主从同步时间超过syncLimit,则丢弃该从节点。

配置完,启动zookeeper

  1. bin/zkServer.sh start

检查是否运行:

  1. echo ruok | nc localhost 2181

1.1连接到zookeeper

  1. $ bin/zkCli.sh -server 127.0.0.1:2181

在shell命令喊中使用help查看命令列表

  1. [zkshell: 0] help

如:

创建一个zookeeper节点:

  1. create /zk_test my_data

获取节点数据

  1. get /zk_test

修改节点数据

  1. set /zk_test junk

删除节点

  1. delete /zk_test

2.复制模式启动zookeeper

  1. tickTime=2000
  2. dataDir=/var/lib/zookeeper
  3. clientPort=2181
  4. initLimit=5
  5. syncLimit=2
  6. server.1=zoo1:2888:3888
  7. server.2=zoo2:2888:3888
  8. server.3=zoo3:2888:3888

http://blog.csdn.net/a906998248/article/details/50815031 ---集群启动 具体步骤

注意:前一个端口用于leader和follower之间数据交换,后一个端口用于leader选举。

ZooKeeper Programmer's Guide

1. zookeeper数据模型

类似于文件系统,所有的节点都是绝对路径,将文件和目录抽象成znode。和Unix中的文件系统路径格式很想,但是只支持绝对路径,不支持相对路径,也不支持点号(”.”和”..”)。

1.1 znode

维护了数据和ACL改变的版本号,每一次数据改变版本号增加,当有一个client去执行update和delete时候,必须提供一个数据变更版本号,如果与数据不符合,则更新失败。

1.2存储小数据

一般不超过1M,大量数据会花费更多的时间和延迟来完成数据拷贝,由于网络的消耗。

1.3 瞬时节点

随着会话结束而删除

When the session ends the znode is deleted. Because
of this behavior ephemeral znodes are not allowed to have children.

1.4操作的原子性

Znode的数据读写是原子的,要么读或写了完整的数据,要么就失败,不会出现只读或写了部分数据。

1.5 顺序znode

名称中包含Zookeeper指定顺序号的znode。若在创建znode时设置了顺序标识,那么该znode被创建后,名字的后边将会附加一串数字,该数字是由一个单调递增的计数器来生成的。例如,创建节点时传入的path是”/aa/bb”,创建后的则可能是”/aa/bb0002”,再次创建后是”/aa/bb0003”。

Znode的创建模式CreateMode有四种,分别是:EPHEMERAL(短暂的znode)、EPHEMERAL_SEQUENTIAL(短暂的顺序znode)、PERSISTENT(持久的znode)和PERSISTENT_SEQUENTIAL(持久的顺序znode)。如果您已经看过了上篇博文,那么这里的api调用应该是很好理解的,见:http://www.cnblogs.com/leocook/p/zk_0.html

2  zookeeper中的时间

Zxid:zookeeper状态的改变都会有事物id自增来维护。

Version numbers :znode的数据和ACL变更维护。

ticks:zookeeper集群部署时的时间单位。

3 zookeepr  watches

可以在读操作exists、getChildren和getData上设置观察,在执行写操作create、delete和setData将会触发观察事件,当然,在执行写的操作时,也可以选择是否触发znode上设置的观察器,具体可查看相关的api。

当观察的znode被创建、删除或其数据被更新时,设置在exists上的观察将会被触发;

当观察的znode被删除或数据被更新时,设置在getData上的观察将会被触发;

当观察的znode的子节点被创建、删除或znode自身被删除时,设置在getChildren上的观察将会被触发,可通过观察事件的类型来判断被删除的是znode还是它的子节点。

注意:在收到收到触发事件到执行读操作之间,znode的状态可能会发生状态,这点需要牢记。

4 ACL

ACL权限:

• CREATE: you can create a child node
• READ: you can get data from a node and list its children.
• WRITE: you can set data for a node
• DELETE: you can delete a child node
• ADMIN: you can set permissions

身份验证模式:

• world 无验证

• auth 只能使用sessionID

• digest  username:password 验证

• ip 客户端IP验证

• host 客户端主机名验证

添加验证

可使用zk对象的addAuthInfo()方法来添加验证模式,如使用digest模式进行身份验证:zk.addAuthInfo(“digest”,”username:passwd”.getBytes());

在zookeeper对象被创建时,初始化会被添加world验证模式。world身份验证模式的验证id是”anyone”。

若该连接创建了znode,那么他将会被添加auth身份验证模式的验证id是””,即空字符串,这里将使用sessionID进行验证。

创建自定义验证:创建ACL对象时,可用ACL类的构造方法ACL(int perms, Id id)

5 zookeeper  内部原理

  • 选举领导

集群中所有的zk实例会选举出来一个“领导实例”(leader),其它实例称之为“随从实例”(follower)。如果leader出现故障,其余的实例会选出一台leader,并一起提供服务,若之前的leader恢复正常,便成为follower。选举follower是一个很快的过程,性能影响不明显。

Leader主要功能是协调所有实例实现写操作的原子性,即:所有的写操作都会转发给leader,然后leader会将更新广播给所有的follower,当半数以上的实例都完成写操作后,leader才会提交这个写操作,随后客户端会收到写操作执行成功的响应。

  • 原子广播

上边已经说到:所有的写操作都会转发给leader,然后leader会将更新广播给所有的follower,当半数以上的实例都完成写操作后,leader才会提交这个写操作,随后客户端会收到写操作执行成功的响应。这么来的话,就实现了客户端的写操作的原子性,每个写操作要么成功要么失败。逻辑和数据库的两阶段提交协议很像。

Znode的每次写操作都相当于数据库里的一次事务提交,每个写操作都有个全局唯一的ID,称为:zxid(ZooKeeper Transaction)。ZooKeeper会根据写操作的zxid大小来对操作进行排序,zxid小的操作会先执行。zk下边的这些特性保证了它的数据一致性:

  • 顺序一致性

任意客户端的写操作都会按其发送的顺序被提交。如果一个客户端把某znode的值改为a,然后又把值改为b(后面没有其它任何修改),那么任何客户端在读到值为b之后都不会再读到a。

  • 原子性

这一点再前面已经说了,写操作只有成功和失败两种状态,不存在只写了百分之多少这么一说。

  • 单一系统映像

客户端只会连接host列表中状态最新的那些实例。如果正在连接到的实例挂了,客户端会尝试重新连接到集群中的其他实例,那么此时滞后于故障实例的其它实例都不会接收该连接请求,只有和故障实例版本相同或更新的实例才接收该连接请求。

  • 持久性

写操作完成之后将会被持久化存储,不受服务器故障影响。

  • 及时性

在对某个znode进行读操作时,应该先执行sync方法,使得读操作的连接所连的zk实例能与leader进行同步,从而能读到最新的类容。

注意:sync调用是异步的,无需等待调用的返回,zk服务器会保证所有后续的操作会在sync操作完成之后才执行,哪怕这些操作是在执行sync之前被提交的。

java构建zookeeper应用

http://www.cnblogs.com/ggjucheng/p/3370359.html

String create(String path, byte[] data, List<ACL> acl,CreateMode createMode) 创建一个给定的目录节点 path, 并给它设置数据,CreateMode 标识有四种形式的目录节点,分别是 PERSISTENT:持久化目录节点,这个目录节点存储的数据不会丢失;PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名;EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session 超时,这种节点会被自动删除;EPHEMERAL_SEQUENTIAL:临时自动编号节点
Stat exists(String path, boolean watch) 判断某个 path 是否存在,并设置是否监控这个目录节点,这里的 watcher 是在创建 ZooKeeper 实例时指定的 watcher,exists方法还有一个重载方法,可以指定特定的watcher
Stat exists(String path,Watcher watcher) 重载方法,这里给某个目录节点设置特定的 watcher,Watcher 在 ZooKeeper 是一个核心功能,Watcher 可以监控目录节点的数据变化以及子目录的变化,一旦这些状态发生变化,服务器就会通知所有设置在这个目录节点上的 Watcher,从而每个客户端都很快知道它所关注的目录节点的状态发生变化,而做出相应的反应
void delete(String path, int version) 删除 path 对应的目录节点,version 为 -1 可以匹配任何版本,也就删除了这个目录节点所有数据
List<String>getChildren(String path, boolean watch) 获取指定 path 下的所有子目录节点,同样 getChildren方法也有一个重载方法可以设置特定的 watcher 监控子节点的状态
Stat setData(String path, byte[] data, int version) 给 path 设置数据,可以指定这个数据的版本号,如果 version 为 -1 怎可以匹配任何版本
byte[] getData(String path, boolean watch, Stat stat) 获取这个 path 对应的目录节点存储的数据,数据的版本等信息可以通过 stat 来指定,同时还可以设置是否监控这个目录节点数据的状态
voidaddAuthInfo(String scheme, byte[] auth) 客户端将自己的授权信息提交给服务器,服务器将根据这个授权信息验证客户端的访问权限。
Stat setACL(String path,List<ACL> acl, int version) 给某个目录节点重新设置访问权限,需要注意的是 Zookeeper 中的目录节点权限不具有传递性,父目录节点的权限不能传递给子目录节点。目录节点 ACL 由两部分组成:perms 和 id。
Perms 有 ALL、READ、WRITE、CREATE、DELETE、ADMIN 几种 
而 id 标识了访问目录节点的身份列表,默认情况下有以下两种:
ANYONE_ID_UNSAFE = new Id("world", "anyone") 和 AUTH_IDS = new Id("auth", "") 分别表示任何人都可以访问和创建者拥有访问权限。
List<ACL>getACL(String path,Stat stat) 获取某个目录节点的访问权限列表

关闭防火墙:Exception in thread “main” org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for

  1. public class TestZk {
  2. public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
  3. // 创建一个与服务器的连接
  4. ZooKeeper zk = new ZooKeeper("192.168.64.10:2181", ClientBase.CONNECTION_TIMEOUT, new Watcher() {
  5. // 监控所有被触发的事件
  6. public void process(WatchedEvent event) {
  7. System.out.println("已经触发了" + event.getType() + "事件!");
  8. }
  9. });
  10. // 创建一个目录节点 ==>已经触发了 None 事件!
  11. zk.create("/testRootPath", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
  12. // 创建一个子目录节点
  13. zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(), Ids.OPEN_ACL_UNSAFE,
  14. CreateMode.PERSISTENT);
  15.  
  16. // testRootData 此处false 不触发事件
  17. System.out.println(new String(zk.getData("/testRootPath", false, null)));
  18.  
  19. // 取出子目录节点列表 ==>[testChildPathOne] 在节点/testRootPath的getChildren上设置观察
  20. System.out.println(zk.getChildren("/testRootPath", true));
  21.  
  22. // 修改子目录节点数据 由于上面的修改数据不触发观察 这边不执行事件
  23. zk.setData("/testRootPath/testChildPathOne", "modifyChildDataOne".getBytes(), -1);
  24.  
  25. // 目录节点状态:[5,5,1281804532336,1281804532336,0,1,0,0,12,1,6]
  26. System.out.println("目录节点状态:[" + zk.exists("/testRootPath", true) + "]");
  27.  
  28. // 创建另外一个子目录节点 ==>已经触发了 NodeChildrenChanged 事件!
  29. zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(), Ids.OPEN_ACL_UNSAFE,
  30. CreateMode.PERSISTENT);
  31.  
  32. // testChildDataTwo
  33. System.out.println(new String(zk.getData("/testRootPath/testChildPathTwo", true, null)));
  34.  
  35. // 删除子目录节点 已经触发了 NodeDeleted 事件!
  36.  
  37. zk.delete("/testRootPath/testChildPathTwo", -1);
  38. zk.delete("/testRootPath/testChildPathOne", -1);
  39. // 删除父目录节点 已经触发了 NodeDeleted 事件!
  40. zk.delete("/testRootPath", -1);
  41. // 关闭连接
  42. zk.close();
  43. }
  44. }

运行结果

ZooKeeper 典型的应用场景

Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似 Master/Slave 管理模式,关于 Zookeeper 的详细架构等内部细节可以阅读 Zookeeper 的源码

下面详细介绍这些典型的应用场景,也就是 Zookeeper 到底能帮我们解决那些问题?下面将给出答案。

1 统一命名服务(Name Service)

分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,既对人友好又不会重复。说到这里你可能想到了 JNDI,没错 Zookeeper 的 Name Service 与 JNDI 能够完成的功能是差不多的,它们都是将有层次的目录结构关联到一定资源上,但是 Zookeeper 的 Name Service 更加是广泛意义上的关联,也许你并不需要将名称关联到特定资源上,你可能只需要一个不会重复名称,就像数据库中产生一个唯一的数字主键一样。

Name Service 已经是 Zookeeper 内置的功能,你只要调用 Zookeeper 的 API 就能实现。如调用 create 接口就可以很容易创建一个目录节点。

2 配置管理(Configuration Management)

配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。

像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中.

 配置管理结构图

 

3 集群管理(Group Membership)

Zookeeper 能够很容易的实现集群管理的功能,如有多台 Server 组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道。

Zookeeper 不仅能够帮你维护当前的集群中机器的服务状态,而且能够帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另一个功能 Leader Election。

它们的实现方式都是在 Zookeeper 上创建一个 EPHEMERAL 类型的目录节点,然后每个 Server 在它们创建目录节点的父目录节点上调用 getChildren(String path, boolean watch) 方法并设置 watch 为 true,由于是 EPHEMERAL 目录节点,当创建它的 Server 死去,这个目录节点也随之被删除,所以 Children 将会变化,这时 getChildren上的 Watch 将会被调用,所以其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是同样的原理。

Zookeeper 如何实现 Leader Election,也就是选出一个 Master Server。和前面的一样每台 Server 创建一个 EPHEMERAL 目录节点,不同的是它还是一个 SEQUENTIAL 目录节点,所以它是个 EPHEMERAL_SEQUENTIAL 目录节点。之所以它是 EPHEMERAL_SEQUENTIAL 目录节点,是因为我们可以给每台 Server 编号,我们可以选择当前是最小编号的 Server 为 Master,假如这个最小编号的 Server 死去,由于是 EPHEMERAL 节点,死去的 Server 对应的节点也被删除,所以当前的节点列表中又出现一个最小编号的节点,我们就选择这个节点为当前 Master。这样就实现了动态选择 Master,避免了传统意义上单 Master 容易出现单点故障的问题。

 集群管理结构图

 

Leader Election 关键代码

  1. package net.xulingbo.zookeeper;
  2.  
  3. import org.apache.log4j.xml.DOMConfigurator;
  4. import org.apache.zookeeper.WatchedEvent;
  5. import org.apache.zookeeper.Watcher;
  6. import org.apache.zookeeper.ZooKeeper;
  7.  
  8. import java.io.IOException;
  9.  
  10. /**
  11. *
  12. * @author gaojiay
  13. *
  14. */
  15. public class TestMainClient implements Watcher {
  16. protected static ZooKeeper zk = null;
  17. protected static Integer mutex;
  18. int sessionTimeout = ;
  19. protected String root;
  20.  
  21. public TestMainClient(String connectString) {
  22. if(zk == null){
  23. try {
  24.  
  25. String configFile = this.getClass().getResource("/").getPath()+"net/xulingbo/zookeeper/log4j/log4j.xml";
  26. DOMConfigurator.configure(configFile);
  27. System.out.println("创建一个新的连接:");
  28. zk = new ZooKeeper(connectString, sessionTimeout, this);
  29. mutex = new Integer(-);
  30. } catch (IOException e) {
  31. zk = null;
  32. }
  33. }
  34. }
  35. synchronized public void process(WatchedEvent event) {
  36. synchronized (mutex) {
  37. mutex.notify();
  38. }
  39. }
  40. }
  1. public class LeaderElection extends TestMainClient {
  2. public static final Logger logger = Logger.getLogger(LeaderElection.class);
  3.  
  4. public LeaderElection(String connectString, String root) {
  5. super(connectString);
  6. this.root = root;
  7. if (zk != null) {
  8. try {
  9. Stat s = zk.exists(root, false);
  10. if (s == null) {
  11. zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
  12. }
  13. } catch (KeeperException e) {
  14. logger.error(e);
  15. } catch (InterruptedException e) {
  16. logger.error(e);
  17. }
  18. }
  19. }
  20.  
  21. void findLeader() throws InterruptedException, UnknownHostException, KeeperException {
  22. byte[] leader = null;
  23. try {
  24. //获取leader目录数据
  25. leader = zk.getData(root + "/leader", true, null);
  26. } catch (KeeperException e) {
  27. if (e instanceof KeeperException.NoNodeException) {
  28. logger.error(e);
  29. } else {
  30. throw e;
  31. }
  32. }
  33. if (leader != null) { //如果该集群中存在/leader目录,说明master还没有宕机
  34. following(); //则该连接的主机为从节点
  35. } else { //集群中没有/leader目录,则新建该临时节点(最先执行该方法的zk客户端将创建该leader目录)
  36. String newLeader = null;
  37. byte[] localhost = InetAddress.getLocalHost().getAddress();
  38. try {
  39. newLeader = zk.create(root + "/leader", localhost, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
  40. } catch (KeeperException e) {
  41. if (e instanceof KeeperException.NodeExistsException) {
  42. logger.error(e);
  43. } else {
  44. throw e;
  45. }
  46. }
  47. if (newLeader != null) {
  48. leading(); //成为leader
  49. } else {
  50. mutex.wait();
  51. }
  52. }
  53. }
  54.  
  55. @Override
  56. public void process(WatchedEvent event) {
  57. if (event.getPath().equals(root + "/leader") && event.getType() == Event.EventType.NodeCreated) {
  58. System.out.println("得到通知");
  59. super.process(event);
  60. following();
  61. }
  62. }
  63.  
  64. void leading() {
  65. System.out.println("成为领导者");
  66. }
  67.  
  68. void following() {
  69. System.out.println("成为组成员");
  70. }
  71.  
  72. public static void main(String[] args) {
  73. TestMainServer.start();
  74. String connectString = "localhost:" + TestMainServer.CLIENT_PORT;
  75.  
  76. LeaderElection le = new LeaderElection(connectString, "/GroupMembers");
  77. try {
  78. le.findLeader();
  79. } catch (Exception e) {
  80. logger.error(e);
  81. }
  82. }
  83. }

 4 Zookeeper 实现 Locks 

  1. /*
  2. 加锁:
  3. ZooKeeper 将按照如下方式实现加锁的操作:
  4. 1 ) ZooKeeper 调用 create ()方法来创建一个路径格式为“ _locknode_/lock- ”的节点,此节点类型为sequence (连续)和 ephemeral (临时)。也就是说,创建的节点为临时节点,并且所有的节点连续编号,即“ lock-i ”的格式。
  5. 2 )在创建的锁节点上调用 getChildren ()方法,来获取锁目录下的最小编号节点,并且不设置 watch 。
  6. 3 )步骤 2 中获取的节点恰好是步骤 1 中客户端创建的节点,那么此客户端获得此种类型的锁,然后退出操作。
  7. 4 )客户端在锁目录上调用 exists ()方法,并且设置 watch 来监视锁目录下比自己小一个的连续临时节点的状态。
  8. 5 )如果监视节点状态发生变化,则跳转到第 2 步,继续进行后续的操作,直到退出锁竞争。
  9.  
  10. 解锁:
  11. ZooKeeper 解锁操作非常简单,客户端只需要将加锁操作步骤 1 中创建的临时节点删除即可
  12. */
  13. public class Locks extends TestMainClient {
  14. public static final Logger logger = Logger.getLogger(Locks.class);
  15. String myZnode;
  16.  
  17. public Locks(String connectString, String root) {
  18. super(connectString);
  19. this.root = root;
  20. if (zk != null) {
  21. try {
  22. //创建锁节点,并不设置观察
  23. Stat s = zk.exists(root, false);
  24. if (s == null) {
  25. zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
  26. }
  27. } catch (KeeperException e) {
  28. logger.error(e);
  29. } catch (InterruptedException e) {
  30. logger.error(e);
  31. }
  32. }
  33. }
  34. void getLock() throws KeeperException, InterruptedException{
  35. List<String> list = zk.getChildren(root, false);
  36. String[] nodes = list.toArray(new String[list.size()]);
  37. //对锁目录下 的所有子节点排序
  38. Arrays.sort(nodes);
  39. //判断该zkclient创建的临时顺序节点是否为集群中最小的节点
  40. if(myZnode.equals(root+"/"+nodes[0])){
  41. doAction();
  42. }
  43. else{
  44. waitForLock(nodes[0]);
  45. }
  46. }
  47. //创建zk客户端的临时瞬时节点,并尝试获取锁
  48. void check() throws InterruptedException, KeeperException {
  49. myZnode = zk.create(root + "/lock_" , new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
  50. getLock();
  51. }
  52. void waitForLock(String lower) throws InterruptedException, KeeperException {
  53. Stat stat = zk.exists(root + "/" + lower,true);
  54. if(stat != null){ //发现最小的目录节点还未被移除,则等待
  55. mutex.wait();
  56. }
  57. else{
  58. getLock();
  59. }
  60. }
  61. @Override //发现有节点移除,该等待状态的客户端被notify
  62. public void process(WatchedEvent event) {
  63. if(event.getType() == Event.EventType.NodeDeleted){
  64. System.out.println("得到通知");
  65. super.process(event);
  66. doAction();
  67. }
  68. }
  69. /**
  70. * 执行其他任务
  71. */
  72. private void doAction(){
  73. System.out.println("同步队列已经得到同步,可以开始执行后面的任务了");
  74. }
  75.  
  76. public static void main(String[] args) {
  77. TestMainServer.start();
  78. String connectString = "localhost:"+TestMainServer.CLIENT_PORT;
  79.  
  80. Locks lk = new Locks(connectString, "/locks");
  81. try {
  82. lk.check();
  83. } catch (InterruptedException e) {
  84. logger.error(e);
  85. } catch (KeeperException e) {
  86. logger.error(e);
  87. }
  88. }
  89.  
  90. }

5 同步队列

同步队列用 Zookeeper 实现的实现思路如下:

创建一个父目录 /synchronizing,每个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,然后每个成员都加入这个队列,加入队列的方式就是创建 /synchronizing/member_i 的临时目录节点,然后每个成员获取 / synchronizing 目录的所有目录节点,也就是 member_i。判断 i 的值是否已经是成员的个数,如果小于成员个数等待 /synchronizing/start 的出现,如果已经相等就创建 /synchronizing/start。

  1. /**
  2. * 当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。
  3. */
  4. public class Synchronizing extends TestMainClient {
  5. int size;
  6. String name;
  7. public static final Logger logger = Logger.getLogger(Synchronizing.class);
  8.  
  9. /**
  10. * 构造函数
  11. *
  12. * @param connectString
  13. * 服务器连接
  14. * @param root
  15. * 根目录
  16. * @param size
  17. * 队列大小
  18. */
  19. Synchronizing(String connectString, String root, int size) {
  20. super(connectString);
  21. this.root = root;
  22. this.size = size;
  23.  
  24. if (zk != null) {
  25. try {
  26. Stat s = zk.exists(root, false);
  27. if (s == null) {
  28. zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
  29. CreateMode.PERSISTENT);
  30. }
  31. } catch (KeeperException e) {
  32. logger.error(e);
  33. } catch (InterruptedException e) {
  34. logger.error(e);
  35. }
  36. }
  37. try {
  38. name = new String(InetAddress.getLocalHost().getCanonicalHostName()
  39. .toString());
  40. } catch (UnknownHostException e) {
  41. logger.error(e);
  42. }
  43.  
  44. }
  45.  
  46. /**
  47. * 加入队列
  48. *
  49. * @return
  50. * @throws KeeperException
  51. * @throws InterruptedException
  52. */
  53.  
  54. void addQueue() throws KeeperException, InterruptedException {
  55. zk.exists(root + "/start", true);
  56. zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,
  57. CreateMode.EPHEMERAL_SEQUENTIAL);
  58. synchronized (mutex) {
  59. List<String> list = zk.getChildren(root, false);
  60. //如果队列中子节点数小于size,则等待,如果不小于size,则创建start目录,其他client则触发事件,执行doAction
  61. if (list.size() < size) {
  62. mutex.wait();
  63. } else {
  64. zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE,
  65. CreateMode.PERSISTENT);
  66. }
  67. }
  68. }
  69.  
  70. @Override
  71. public void process(WatchedEvent event) {
  72. if (event.getPath().equals(root + "/start")
  73. && event.getType() == Event.EventType.NodeCreated) {
  74. System.out.println("得到通知");
  75. super.process(event);
  76. doAction();
  77. }
  78. }
  79.  
  80. /**
  81. * 执行其他任务
  82. */
  83. private void doAction() {
  84. System.out.println("同步队列已经得到同步,可以开始执行后面的任务了");
  85. }
  86.  
  87. public static void main(String args[]) {
  88. // 启动Server
  89. TestMainServer.start();
  90. String connectString = "localhost:" + TestMainServer.CLIENT_PORT;
  91. int size = 1;
  92. Synchronizing b = new Synchronizing(connectString, "/synchronizing",
  93. size);
  94. try {
  95. b.addQueue();
  96. } catch (KeeperException e) {
  97. logger.error(e);
  98. } catch (InterruptedException e) {
  99. logger.error(e);
  100. }
  101. }
  102. }

6 FIFO队列(生产者-消费者)

  1. /**
  2. * 队列按照 FIFO 方式进行入队和出队操作,例如实现生产者和消费者模型。
  3. *
  4. * 实现的思路也非常简单,就是在特定的目录下创建 SEQUENTIAL 类型的子目录
  5. * /queue_i,这样就能保证所有成员加入队列时都是有编号的,出队列时通过 getChildren( )
  6. * 方法可以返回当前所有的队列中的元素,然后消费其中最小的一个,这样就能保证 FIFO。
  7. */
  8. public class FIFOQueue extends TestMainClient {
  9. public static final Logger logger = Logger.getLogger(FIFOQueue.class);
  10.  
  11. /**
  12. * Constructor
  13. *
  14. * @param connectString
  15. * @param root
  16. */
  17. FIFOQueue(String connectString, String root) {
  18. super(connectString);
  19. this.root = root;
  20. if (zk != null) {
  21. try {
  22. Stat s = zk.exists(root, false);
  23. if (s == null) {
  24. zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
  25. CreateMode.PERSISTENT);
  26. }
  27. } catch (KeeperException e) {
  28. logger.error(e);
  29. } catch (InterruptedException e) {
  30. logger.error(e);
  31. }
  32. }
  33. }
  34.  
  35. /**
  36. * 生产者
  37. *
  38. * @param i
  39. * @return
  40. */
  41.  
  42. boolean produce(int i) throws KeeperException, InterruptedException {
  43. ByteBuffer b = ByteBuffer.allocate(4);
  44. byte[] value;
  45. b.putInt(i);
  46. value = b.array();
  47. zk.create(root + "/element", value, ZooDefs.Ids.OPEN_ACL_UNSAFE,
  48. CreateMode.PERSISTENT_SEQUENTIAL);
  49. return true;
  50. }
  51.  
  52. /**
  53. * 消费者
  54. *
  55. * @return
  56. * @throws KeeperException
  57. * @throws InterruptedException
  58. */
  59. int consume() throws KeeperException, InterruptedException {
  60. int retvalue = -1;
  61. Stat stat = null;
  62. while (true) {
  63. synchronized (mutex) {
  64. // 对root的子节点设置监听
  65. List<String> list = zk.getChildren(root, true);
  66. // 如果没有任何子节点,则wait
  67. if (list.size() == 0) {
  68. mutex.wait();
  69. } else {
  70. Integer min = new Integer(list.get(0).substring(7));
  71. for (String s : list) {
  72. Integer tempValue = new Integer(s.substring(7));
  73. if (tempValue < min)
  74. min = tempValue;
  75. }
  76. byte[] b = zk.getData(root + "/element" + min, false, stat);
  77. // 获取到子节点数据之后 执行delete,并触发事件,执行所有cliet的notify
  78. zk.delete(root + "/element" + min, 0);
  79. ByteBuffer buffer = ByteBuffer.wrap(b);
  80. retvalue = buffer.getInt();
  81. return retvalue;
  82. }
  83. }
  84. }
  85. }
  86.  
  87. @Override
  88. public void process(WatchedEvent event) {
  89. super.process(event);
  90. }
  91.  
  92. public static void main(String args[]) {
  93. // 启动Server
  94. TestMainServer.start();
  95. String connectString = "localhost:" + TestMainServer.CLIENT_PORT;
  96.  
  97. FIFOQueue q = new FIFOQueue(connectString, "/app1");
  98. int i;
  99. Integer max = new Integer(5);
  100.  
  101. System.out.println("Producer");
  102. for (i = 0; i < max; i++)
  103. try {
  104. q.produce(10 + i);
  105. } catch (KeeperException e) {
  106. logger.error(e);
  107. } catch (InterruptedException e) {
  108. logger.error(e);
  109. }
  110.  
  111. for (i = 0; i < max; i++) {
  112. try {
  113. int r = q.consume();
  114. System.out.println("Item: " + r);
  115. } catch (KeeperException e) {
  116. i--;
  117. logger.error(e);
  118. } catch (InterruptedException e) {
  119. logger.error(e);
  120. }
  121. }
  122.  
  123. }
  124. }

参考:http://www.cnblogs.com/leocook/p/zk_0.html

zookeeper入门与实践的更多相关文章

  1. Storm实时计算:流操作入门编程实践

    转自:http://shiyanjun.cn/archives/977.html Storm实时计算:流操作入门编程实践   Storm是一个分布式是实时计算系统,它设计了一种对流和计算的抽象,概念比 ...

  2. 《Github入门与实践》读书笔记 蟲咋先生的追求之旅(上)

    <Github入门与实践>作者: [日] 大塚弘记 译者:支鹏浩/刘斌   简介 本书从Git的基本知识和操作方法入手,详细介绍了GitHub的各种功能,GitHub与其他工具或服务的协作 ...

  3. 分布式进阶(十六)Zookeeper入门基础

    Zookeeper入门基础 前言 在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据.如果在创建znode时Flag设置为EPHEMERAL,那么当 ...

  4. Python编程从入门到实践笔记——异常和存储数据

    Python编程从入门到实践笔记——异常和存储数据 #coding=gbk #Python编程从入门到实践笔记——异常和存储数据 #10.3异常 #Python使用被称为异常的特殊对象来管理程序执行期 ...

  5. Python编程从入门到实践笔记——文件

    Python编程从入门到实践笔记——文件 #coding=gbk #Python编程从入门到实践笔记——文件 #10.1从文件中读取数据 #1.读取整个文件 file_name = 'pi_digit ...

  6. Python编程从入门到实践笔记——类

    Python编程从入门到实践笔记——类 #coding=gbk #Python编程从入门到实践笔记——类 #9.1创建和使用类 #1.创建Dog类 class Dog():#类名首字母大写 " ...

  7. Python编程从入门到实践笔记——函数

    Python编程从入门到实践笔记——函数 #coding=gbk #Python编程从入门到实践笔记——函数 #8.1定义函数 def 函数名(形参): # [缩进]注释+函数体 #1.向函数传递信息 ...

  8. Python编程从入门到实践笔记——用户输入和while循环

    Python编程从入门到实践笔记——用户输入和while循环 #coding=utf-8 #函数input()让程序暂停运行,等待用户输入一些文本.得到用户的输入以后将其存储在一个变量中,方便后续使用 ...

  9. Python编程从入门到实践笔记——字典

    Python编程从入门到实践笔记——字典 #coding=utf-8 #字典--放在{}中的键值对:跟json很像 #键和值之间用:分隔:键值对之间用,分隔 alien_0 = {'color':'g ...

随机推荐

  1. Hadoop学习笔记:使用Mrjob框架编写MapReduce

    1.mrjob介绍 一个通过mapreduce编程接口(streamming)扩展出来的Python编程框架. 2.安装方法 pip install mrjob,略.初学,叙述的可能不是很细致,可以加 ...

  2. 如何在shell脚本中导出数组供子进程使用

    功能说明:设置或显示环境变量. 语 法:export [-fnp][变量名称]=[变量设置值] 补充说明:在shell中执行程序时,shell会提供一组环境变量.export可新增,修改或删除环境变量 ...

  3. Java编程风格学习(三)

    在上一篇的java编程风格学习(二)中我们学习了一些在Java编码过程中的格式规范,遵循这些规范毋庸置疑是我们的书写高质量代码的前提与基础.今天我们更进一步,一起来学习Java编程的命名规范,向着编写 ...

  4. MySQL Innodb 并发涉及参数

    1 参数作用 MySQL的各个插件式引擎中,都会对事务及线程做一定的处理和优化.在Innodb引擎中,总是尝试保持 innodb内 操作系统的线程数(暂命名为innodb_thread) 应该小于或等 ...

  5. Android 视频编辑 SDK

    Android 视频编辑 SDK接入说明 一.名词解释 分辨率:用于计算机视频处理的图像,以水平和垂直方向上所能显示的像素数来表示分辨率.常见视频分辨率的有1080P即1920x1080,720P即1 ...

  6. x86主机搭建家庭智能路由系统 ---- Proxmox虚拟化实现一机多用

    Proxmox VE简介 Proxmox VE(Proxmox Virtual Environment) 是一款完全开源虚拟化管理平台,可以管理QEMU/KVM虚拟机和LXC容器.事实上它只是一个前端 ...

  7. [HDU1210] Eddy's 洗牌问题

    Problem Description Eddy是个ACMer,他不仅喜欢做ACM题,而且对于纸牌也有一定的研究,他在无聊时研究发现,如果他有2N张牌,编号为1,2,3..n,n+1,..2n.这也是 ...

  8. 1774: [Usaco2009 Dec]Toll 过路费

    1774: [Usaco2009 Dec]Toll 过路费 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 263  Solved: 154[Submit ...

  9. JavaMail邮件发送不成功的那些坑人情况及分析说明

    [我的Segmentfault原文]https://segmentfault.com/a/1190000008030346 前言   JavaMail的使用本身并不难,网上有不少案例,简单易懂,而且有 ...

  10. 使用Java语言开发微信公众平台(四)——图文消息的发送与响应

    在上一篇文章中,我们实现了被关注回复与关键词回复功能.在用户关注的时候自动推送功能菜单,并根据用户输入的关键词,回复特定信息.但是,我们只能回复文本消息给用户,如何才回复一条图文消息呢?本周,我们一起 ...