首页 > 代码库 > zookeeper介绍

zookeeper介绍

ZooKeeper的简单介绍

ZooKeeper简介

ZooKeeper是一个为分布式应用提供分布式协调功能的开源(github)服务。其主要应用在数据发布与订阅(配置中心),命名服务、分布式锁、集群管理与Master选举、分布式通知等。
在ZooKeeper官网是这样介绍ZooKeeper的:

ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them ,which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed.
上述描述说明像配置管理、命名等这些服务一般被分布式应用程序以某种形式使用,而在使用这些服务时,都会花费大量时间在修复bug和处理竞争条件上。同时也可能发送下述情况:
1. 当有变化发送时,难以调整,管理。
2. 这些服务的实现方式不同导致难以部署。
而ZooKeeper就是为了解决上述问题存在的。它提供了一个统一、简洁的分布式协调服务,同时几乎可在任何平台使用,可参考ZooKeeper管理员指南——部署与管理ZooKeeper。

就像它所提供服务的对象是分布式应用,它自身也是分布式的。ZooKeeper一般会搭建成集群模式来提供服务,其节点个数一般为奇数个,这是由于其有一个重要特性,即:集群中只有有过半机器是正常工作的,对外即是可用的(过半存活即可用)。

数据模型

ZooKeeper数据模型的结构与文件系统十分相似,因此可以看成是一棵树。下面为其结构图
技术分享
其中每个节点可称为ZNode,每个节点都通过其路径唯一标识,如上图所示。每个节点都可以存储数据,它被设计用来存储协同数据,如:状态信息,配置,本地信息等。所以存储的数据不多。

ZNode

ZNode可以分为两类:永久节点和临时性节点(Ephemeral node)。
- 永久节点:需要显式创建和删除,在会话(session,在下面介绍)结束后仍然存在。
- 临时节点:需要显式创建,在删除时,它可以被显式删除,但在使用时,我们一般使用它的特性:在session结束后自动删除。如在集群中可通过这特性判断机器是否宕机断开连接(无法维持session)。
同时还有一个sequential的特性,通过设置此特性,在ZNode的名字后会自动添加自增数字sequenceNo。

每个ZNode都由3部分组成:
1. 状态stat
2. 数据data
3. 孩子节点children

stat

节点状态,对节点的每次改变都会使状态改变,可由状态得知创建顺序等信息。
下面为灾zookeeper.jute中Stat结构

// information shared with the client
    class Stat {
        long czxid;      // created zxid
        long mzxid;      // last modified zxid
        long ctime;      // created
        long mtime;      // last modified
        int version;     // version
        int cversion;    // child version
        int aversion;    // acl version
        long ephemeralOwner; // owner id if ephemeral, 0 otw
        int dataLength;  //length of the data in the node
        int numChildren; //number of children of this node
        long pzxid;      // last modified children
}

可参考
znode的stat数据结构
http://coolxing.iteye.com/blog/1871328

session

client与zk集群通话时需要创建一个session,而每个session都有一个超时时间t(

The current implementation requires that the timeout be a minimum of 2 times the tickTime (as set in the server configuration) and a maximum of 20 times the tickTime.

),若zk在t内看不到与此session相关的信息,那么zk定义此session失效,而对于临时节点这也意味着节点的删除,若设置了监控,那么设置监控的client即可接受到消息。

监控

zk可对每个节点设置监控,监视事件有:
- ZOO_CHANGED_EVENT 当前节点改变时触发,zoo_exists()和zoo_get()设置。
- ZOO_CHILD_EVENT 当前节点的子节点改变时触发,zoo_get_children() 和 zoo_get_children2()设置。
- ZOO_CREATED_EVENT 当前节点创建时触发,zoo_exists()设置。
- ZOO_DELETED_EVENT 当前节点删除时触发,zoo_exists()和zoo_get(),zoo_get_children() 和 zoo_get_children2()设置。
- ZOO_NOTWATCHING_EVENT 取消监控时触发
- ZOO_SESSION_EVENT 会话丢失触发

当监控事件发生便会调用监控回调函数,监控回调有2种:
一为全局监控回调,在调用zookeeper_init时设置,当调用上述函数并设置启用监控,当事件发生时便会调用此函数。
二为局部监控回调,只与当前节点有关。通过形如zoo_wget()设置(添加w)。

监控回调函数模式如下:

typedef void (*watcher_fn)(zhandle_t *zh, int type, int state, const char *path,void *watcherCtx);

note

  1. watcher被触发过一次即会被取消,要重新设置。要实现持续watch,可通过在回调函数中重新设置watch来实现。但在获得事件与再次设置watcher之间的那段时间,节点改变不可知。

    Because watches are one time triggers and there is latency between getting the event and sending a new request to get a watch you cannot reliably see every change that happens to a node in ZooKeeper. Be prepared to handle the case where the znode changes multiple times between getting the event and setting the watch again. (You may not care, but at least realize it may happen.)

  2. 在异步模式下提供其他回调函数,用于在异步请求完成时调用

应用

下面对zk的应用进行一些简单介绍: 参考[ZooKeeper原理及使用 ](http://blog.csdn.net/xinguan1267/article/details/38422149) [ZooKeeper典型应用场景一览](http://www.cnblogs.com/tommyli/p/3766189.html)

数据发布与订阅(配置中心)

数据发布与订阅即是通过把数据发送到zookeeper上,供订阅者动态获取数据,来实现数据的统一管理。现在的使用数据一般为配置信息。其实现一般如下: 1. 把配置写入zookeeper; 2. 应用启动时读取配置并watch; 3. 当配置改变,触发事件,调用回调修改各应用配置,并继续watch。

分布式锁

排它锁

最简单排它锁实现
即在所有试图获取锁的客户端中,只有一个可以成功获得锁。其实现如下: 1. 所有客户端都去创建同一个ZNode(lock),类型EPHEMERAL,创建成功的那个客户端即获得锁。 2. 其它客户端watch那个ZNode。 3. 当ZNode删除,其它客户端在创建。 但此方法会有羊群效应。
羊群效应
当ZNode删除时,所有其它客户端都会收到此事件,但却只有只有一个节点获得锁。
排它锁改进
实现: 1. 创建锁节点/lock 2. 所有客户端都去创建锁节点的子节点,类型为EPHEMERAL_SEQUENCE。 3. 使lock下序号最小节点获取锁。 4. 其它节点watch比它小的节点。 5. 锁释放,则那个唯一监控节点被激活获得锁。 **note:查找资料后发现对于分布式锁的羊群效应用有着不同说法,另一种中羊群效应是由于在判断本节点是否是最小节点时每一个其它节点都需判断(在上级节点设置watch,导致都收到事件)而导致。故改为watch前一节点,可参考[shared locks](http://zookeeper.apache.org/doc/trunk/recipes.html#sc_recipes_Locks)**

共享锁

可参考[shared locks](http://zookeeper.apache.org/doc/trunk/recipes.html#Shared+Locks)

集群管理与Master选举

集群管理是利用临时节点的特性和watch机制,**通过把机器注册为某一永久节点的临时子节点并watch永久节点**,则当机器宕机或因其它原因断开连接,session超时后,节点删除触发事件,或加入新节点也会触发事件,即可知机器变化。 master选举实现方法与上述锁实现方法类似,只是把获得锁的节点改为了成为master的节点。

参考资料

  1. Zookeeper C API 指南
  2. ZooKeeper管理员指南——部署与管理ZooKeeper
  3. ZooKeeper原理及使用
  4. 官方文档
<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    zookeeper介绍