ZooKeeper 拥有一个层次的命名空间。(like distributed)

 

    注意:ZooKeeper 中不许使用相对路径。

 

一    ZooKeeper 数据模型

        1)    Znode

                    ZooKeeper 目录树中的每一个节点对应着一个 Znode 每个 Znode 维护者一个属性结构。

                        dataVersion       版本号

                        ctime                  创建时间

                        mtime                 修改时间

                如果所提供的数据版本号与实际的不匹配,那么这个操作将会失败。

                   Znode 是客户端要访问的 ZooKeeper 实体,包含以下几个特征。

                     Watches     客户端可以在节点上设置 watch (监视器)。当节点状态发生改变时,将会处发 watch 对应的操作。 当 watch 被触发时, Zookeeper 将会向 客户端发送且仅发送一个通知, 因为 watch 只能被触发一次。

                        数据访问  

                        临时节点   ZK 在创建候确定类型且不能被改变(临时节点,永久节点)。临时节点依赖于会话。 一旦会话结束,临时节点删除。 ZK 临时节点不允许拥有子节点。相反,永久节点的生命周期不依赖于会话。只有在客户端执行删除操作时才会被删除。

                        顺序节点    (唯一性保证)创建 Znode 的时候,用户可以请求在 ZK的路径结尾添加一个递增计数。这个计数对于此节点的父节点来说是唯一的。

        2)       ZooKeeper 中的时间

                        Zxid

                        每一个对节点的改变都将产生一个唯一的 zxid。 如果 zxid1 的值小于 zxid2 的值, 那么zxid1 所对应的事件发生在 2 所对应的事件 之前。 实际上, ZooKeeper 的每个节点维护者 3 个 zxid 值,分别为:cZxid、mZxid 和 pZxid。 cZxid 是节点的创建时间锁对应的 Zxid 格式时间戳, mZxid 是节点的修改时间所对应的 Zxid 格式时间戳。

                        版本号

                        对节点的每一个操作都将致使这个节点的版本号增加。 每个节点维护着三个版本号,它们 version (节点数据版本号)、cversion (子节点版本号)、avevsion (节点所拥有的 ACL 的版本号)。

        3)       ZooKeeper 节点属性结构

                        

zookeeper对应版本 zookeeper version_客户端

 

二    ZooKeeper会话及状态

                   1)   服务器与客户端之间会话

                        在ZooKeeper中,客户端和服务端建立连接后,会话随之建立,生成一个全局唯一的会话ID(Session ID)。服务器和客户端之间维持的是一个长连接,在SESSION_TIMEOUT时间内,服务器会确定客户端是否正常连接(客户端会定时向服务器发送heart_beat,服务器重置下次SESSION_TIMEOUT时间)。因此,在正常情况下,Session一直有效,并且ZK集群所有机器上都保存这个Session信息。在出现网络或其它问题情况下(例如客户端所连接的那台ZK机器挂了,或是其它原因的网络闪断),客户端与当前连接的那台服务器之间连接断了,这个时候客户端会主动在地址列表寻址(实例化ZK对象的时候传入构造方法的那个参数connectString)中选择新的地址进行连接。

                   2)    连接断开

                         在这个过程中,两类异常CONNECTIONLOSS(连接断开)和SESSIONEXPIRED(Session过期)。

                     连接断开(CONNECTIONLOSS)一般发生在网络的闪断或是客户端所连接的服务器挂机的时候。ZooKeeper客户端自己会首先感知到这个异常,具体逻辑是在如下方法中触发的:一种场景是Server服务器挂了,这个时候,ZK客户端首选会捕获异常。核心流程如下: ZK客户端捕获“连接断开”异常 ——> 获取一个新的ZK地址 ——> 尝试连接  在这个流程中,我们可以发现,整个过程不需要开发者额外的程序介入,都是ZK客户端自己会进行的,并且,使用的会话ID都是同一个,所以结论就是:发生CONNECTIONLOSS的情况,应用不需要做什么事情,等待ZK客户端建立新的连接即可。

                    3)    会话超时

                         SESSIONEXPIRED发生在上面蓝色文字部分,这个通常是ZK客户端与服务器的连接断了,试图连接上新的ZK机器,但是这个过程如果耗时过长,超过了SESSION_TIMEOUT 后还没有成功连接上服务器,那么服务器认为这个Session已经结束了(服务器无法确认是因为其它异常原因还是客户端主动结束会话),由于在ZK中,很多数据和状态都是和会话绑定的,一旦会话失效,那么ZK就开始清除和这个会话有关的信息,包括这个会话创建的临时节点和注册的所有Watcher。在这之后,由于网络恢复后,客户端可能会重新连接上服务器,但是很不幸,服务器会告诉客户端一个异常:SESSIONEXPIRED(会话过期)。此时客户端的状态变成 CLOSED状态,应用要做的事情就是的看自己应用的复杂程序了,要重新实例zookeeper对象,然后重新操作所有临时数据(包括临时节点和注册Watcher),总之,会话超时在ZK使用过程中是真实存在的。

                            所以这里也简单总结下,一旦发生会话超时,那么存储在ZK上的所有临时数据与注册的订阅者都会被移除,此时需要重新创建一个ZooKeeper客户端实例,需要自己编码做一些额外的处理。

                    4)     会话时间(Session Time)会话超时时间控制先确认 ZooKeeper 客户端设置大小

                            在ZooKeeper API 实例化一个ZK客户端的时候,需要设置一个会话的超时时间。这里需要注意的一点是,客户端并不是可以随意设置这个会话超时时间,在ZK服务器端对会话超时时间是有限制的,主要是minSessionTimeout和maxSessionTimeout这两个参数设置的。(详细查看这个文章《ZooKeeper管理员指南》)Session超时时间限制,如果客户端设置的超时时间不在这个范围,那么会被强制设置为最大或最小时间。 默认的Session超时时间是在2 * tickTime ~ 20 * tickTime。所以,如果应用对于这个会话超时时间有特殊的需求的话,一定要和ZK管理员沟通好,确认好服务端是否设置了对会话时间的限制。

三   ZooKeeper watches (观察者)

                    1)      Zookeeper 可以为所有的读操作设置一个watch,包括getData()、getChildren()和exists()。都有一个开关可以在操作的同时再设置一个watch。(实际上是为了让所有细节都通知到ZooKeeper)在ZooKeeper中,Watch是一个一次性触发器,会在被设置watch的数据发生变化的时候,发送给设置watch的客户端。

                                watch的定义中有三个关键点:

                                    一次性触发器

                                            一个watch事件将会在数据发生变更时发送给客户端。例如,如果客户端执行操作getData(“/znode1″, true),而后/znode1 发生变更或是删除了,客户端都会得到一个/znode1 的watch事件。如果/znode1 再次发生变更,则在客户端没有设置新的watch的情况下,是不会再给这个客户端发送watch事件的。

                                 发送客户端

                                           一个事件会发送给客户端,但可能在操作成功的返回值到达发起变动的客户端之前,这个事件还没有送达watch的客户端。Watch是异步发送的。但ZooKeeper保证了一个顺序:一个客户端在收到watch事件之前,一定不会看到它设置过watch的值的变动。网络时延和其他因素可能会导致不同的客户端看到watch和更新返回值的时间不同。但关键点是,每个客户端所看到的每件事都是有顺序的。(ZooKeeper 为watch 提供了一个有序的一致性)

                                    设置 watch 数据

                                            可以认为ZooKeeper维护了两个watch列表:数据 watch和 子 watch。getData()和exists() 设置 data watch,而 getChildren()设置child watch。或者,可以认为watch是根据返回值设置的。getData()和exists()返回节点本身的信息,而getChildren()返回子节点的列表。一个成功的 setData() 触发znode上设置的data watch。一个 create() 操作会触发被创建的znode上的数据watch,以及其父节点上的child watch。而一个成功的?delete()操作将会同时触发一个znode的data watch和child watch(因为这样就没有子节点了),同时也会触发其父节点的child watch。(需验证)

                                    Watch由client连接上的ZooKeeper服务器在本地维护。减小设置、维护和分发watch的开销。当一个客户端连接到一个新的服务器上时,任何事件都可能会触发 watch 。当与一个服务器失去连接的时候,是无法接收到watch的。而当client重新连接时,如果需要的话,所有先前注册过的watch,都会被重新注册。(通常这是完全透明的。只有在一个特殊情况下,watch可能会丢失:对于一个未创建的znode的exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个watch事件可能会被丢失)。

                

            ZooKeeper对Watch提供了什么保障

                                            Watch与其他事件、其他watch以及异步回复都是有序的。ZooKeeper客户端库保证所有事件都会按顺序分发。

                                            客户端会保障它在看到相应的znode的新数据之前接收到watch事件。

                                            从ZooKeeper接收到的watch事件顺序一定和ZooKeeper服务所看到的事件顺序是一致的。

                                            Watch是一次性触发器,如果你得到了一个watch事件,而你希望在以后发生变更时继续得到通知,你应该再设置一个watch。

                                            因为watch是一次性触发器,而获得事件再发送一个新的设置watch的请求这一过程会有延时,所以你无法确保你看到了所有发生在ZooKeeper上的一个节点上的事件。所以请处理好在这个时间窗口中可能会发生多次znode变更的这种情况。(你可以不处理,但至少请认识到这一点)。

                                            

 

四  ZooKeeper ACL(父子节点权限独立。

                    1)  在Zookeeper中,node的ACL是没有继承关系的,是独立控制的。 zk的节点可以扩展。

                            

                        

zookeeper对应版本 zookeeper version_客户端_02

 

 

五 ZooKeeper 一致性

Zookeeper的实现是有Client、Server构成,Server端提供了一个一致性复制、存储服务,Client端会提供一些具体的语义,比如分布式锁、选举算法、分布式互斥等。从存储内容来说,Server端更多的是存储一些数据的状态,而非数据内容本身,因此Zookeeper可以作为一个小文件系统使用。 数据状态的存储量相对不大,完全可以全部加载到内存中,从而极大地消除了通信延迟。

                        Server可以崩溃后(Crash)重启。考虑到容错性,Server必须“记住”之前的数据状态,因此数据需要持久化,但吞吐量很高时,磁盘的IO便成为系统瓶颈,其解决办法是使用缓存,把随机写变为连续写。

 

  • 全序(Total order):如果消息a在消息b之前发送,则所有Server应该看到相同的结果
  • 因果顺序(Causal order):如果消息a在消息b之前发生(a导致了b),并被一起发送,则a始终在b之前被执行。 (也就是说永远都是顺序加载。)

使用TCP协议保证了消息的全序特性(先发先到),通过Leader解决了因果顺序问题:先到Leader的先执行。因为有了Leader,Zookeeper的架构就变为:Master-Slave模式,但在该模式中Master(Leader)会Crash,因此,Zookeeper引入了Leader选举算法,以保证系统的健壮性。归纳起来Zookeeper整个工作分两个阶段:


                1. Atomic Broadcast (原子性保证)

                    同一时刻存在一个Leader节点,其他节点称为“Follower”,

                    写 :

                        Leader->执行-watches 

                        客户端->更新->Leader->执行-watches 

                        客户端->更新-> Follower->Leader->执行-watches   

                     读 (无特殊要求) :

                           Client->Follower

                           Client->Leader

                        读 (最新) :

                           Client->leader

 

            Zookeeper设计的读写比例是2:1。

  • 因为只有一个Leader,Leader提交到Follower的请求一定会被接受(没有其他Leader干扰)
  • 不需要所有的Follower都响应成功,只要一个多数派即可
  • 如果这个时候有个渣渣Leader复活了,那么这个Leader的属性里面,会有一个

通俗地说,如果有2f+1个节点,允许f个节点失败。

        A Leader  B 死了 - C 正常。   不用选举。 A死 选举失败

 

 

六 ZooKeeper Leader选举

                   Leader Election

                            Leader选举主要是依赖Paxos算法,这里仅考虑Leader选举带来的一些问题。Leader选举遇到的最大问题是,”新老交互“的问题,新Leader是否要继续老Leader的状态。这里要按老Leader Crash的时机点分几种情况:

  1. 老Leader在COMMIT前Crash(已经提交到本地)
  2. 老Leader在COMMIT后Crash,但有部分Follower接收到了Commit请求

第一种情况,这些数据只有老Leader自己知道,当老Leader重启后,需要与新Leader同步并把这些数据从本地删除,以维持状态一致。

第二种情况,新Leader应该能通过一个多数派获得老Leader提交的最新数据

 

老Leader重启后,可能还会认为自己是Leader,可能会继续发送未完成的请求,从而因为两个Leader同时存在导致算法过程失败,解决办法是把Leader信息加入每条消息的id中,Zookeeper中称为zxid,zxid为一64位数字,高32位为leader信息又称为epoch,每次leader转换时递增;低32位为消息编号,Leader转换时应该从0重新开始编号。通过zxid,Follower能很容易发现请求是否来自老Leader,从而拒绝老Leader的请求。

 

因为在老Leader中存在着数据删除(情况1),因此Zookeeper的数据存储要支持补偿操作,这也就需要像数据库一样记录log。

 

 

七 ZooKeeper 锁 同其他锁一样。

                        

zookeeper对应版本 zookeeper version_数据_03

 

 

 

 

 

 

下面是一些 zookeeper 在 hadoop 生态圈的一个形态

 

 

 

zookeeper对应版本 zookeeper version_zookeeper对应版本_04

 

 

zookeeper对应版本 zookeeper version_服务器_05

 

zookeeper对应版本 zookeeper version_数据_06

 

 

zookeeper对应版本 zookeeper version_客户端_07

 

 

1.经过Map、Reduce运算后产生的结果看上去是被写入到HBase了,但是其实HBase中HLog和StoreFile中的文件在进行flush to disk操作时,这两个文件存储到了HDFS的DataNode中,HDFS才是永久存储。

2.ZooKeeper跟Hadoop Core、HBase有什么关系呢?ZooKeeper都提供了哪些服务呢?主要有:管理Hadoop集群中的NameNode,HBase中HBaseMaster的选举,Servers之间状态同步等。具体一点,细一点说,单只HBase中ZooKeeper实例负责的工作就有:存储HBase的Schema,实时监控HRegionServer,存储所有Region的寻址入口,当然还有最常见的功能就是保证HBase集群中只有一个Master。

 

 

 

 

 

ZooKeeper 将按照如下方式实现加锁的操作:



1 ) ZooKeeper 调用 create ()方法来创建一个路径格式为“ _locknode_/lock- ”的节点,此节点类型为sequence (连续)和 ephemeral (临时)。也就是说,创建的节点为临时节点,并且所有的节点连续编号,即“lock-i ”的格式。





2 )在创建的锁节点上调用 getChildren ()方法,来获取锁目录下的最小编号节点,并且不设置 watch 。





3 )步骤 2 中获取的节点恰好是步骤 1 中客户端创建的节点,那么此客户端获得此种类型的锁,然后退出操作。





4 )客户端在锁目录上调用 exists ()方法,并且设置 watch 来监视锁目录下比自己小一个的连续临时节点的状态。





5 )如果监视节点状态发生变化,则跳转到第 2 步,继续进行后续的操作,直到退出锁竞争。



 

 

 

 

 

 

                                                                                                                        God has given me a gift. Only one. I am the most complete fighter in the world. My whole life, I have trained. I must prove I am worthy of someting.                                                             rocky_24

 

God has given me a gift. Only one. I am the most complete fighter in the world. My whole life, I have trained. I must prove I am worthy of someting. rocky_24