ZK入门笔记

1. 什么是ZK
       ZooKeeper(简称ZK)是一种用于分布式应用程序的高性能协调服务。它在一个简单的界面中公开了常用服务 - 例如命名(naming),配置管理(configuration),

    同步(synchronization)和组服务(group services)因此你不必从头开始编写它们。你可以使用现成的方法来实现共识(consensus),组管理(group management),

    领导者选举(leader election)和在线协议(presence protocols)。您可以根据自己的特定需求进行构建。

    官网:http://zookeeper.apache.org/

 2. ZK担保(官方提到ZK能做到什么)

      ZooKeeper非常快速而且非常简单。但是,由于其目标是构建更复杂的服务(如同步)的基础,因此它提供了一系列保证。这些是:

担保

解释

顺序一致性

客户端的更新将按发送顺序应用。

原子性

更新要么成功,要么失败。没有部分(局部)结果。

单系统映像

无论服务器连接到哪个服务器,客户端都将看到相同的服务视图。

可靠性

一旦应用了更新,它将从那时起持续到客户端覆盖更新。

及时性

系统的客户视图保证在特定的时间范围内是最新的。

3. ZK环境搭建

       参照官网教程  http://zookeeper.apache.org/doc/current/zookeeperStarted.html

       或者 https://blog.csdn.net/tlk20071/article/details/52028945

4. ZK相关的基础知识(

       1)会话(Session                   

  • Session 指的是 ZooKeeper  服务器与客户端会话。在 ZooKeeper 中,一个客户端连接是指客户端和服务器之间的一个 TCP 长连接
  • 客户端启动的时候,首先会与服务器建立一个 TCP 连接,从第一次连接建立开始,客户端会话的生命周期也开始了。
  • 通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够 Zookeeper 服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的 Watch 事件通知
  • Session sessionTimeout 用来设置一个客户端会话(Session)的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因
    导致客户端连接断开时,只要在 sessionTimeout 规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效
  • 在为客户端创建会话之前,服务端首先会为每个客户端都分配一个 sessionID。由于 sessionID Zookeeper 会话的一个重要标识,许多与会话相关的运行机制
    都是基于这个 sessionID 的。因此,无论是哪台服务器为客户端分配的 sessionID,都务必保证全局唯一
  • 在实际的应用中,如果Client与Server之间的通信足够频繁,Session的维护就不需要其它额外的消息了。否则,ZooKeeper Client会每t/3 ms发一次心跳给Server,如果Client 2t/3 ms没收到来自Server的心跳回应,就会换到一个新的ZooKeeper Server上。这里t是用户配置的Session的超时时间。

       2)节点(znode)

虽然zookeeper的实现比较复杂,但是它提供的模型抽象却是非常简单的。Zookeeper提供一个多层级的节点命名空间(节点称为znode),每个节点都用一个以

    Regular ZNode: 常规型ZNode, 用户需要显式的创建、删除。

    Ephemeral ZNode: 临时型ZNode, 用户创建它之后,可以显式的删除,也可以在创建它的Session结      

        束后,由Z ooKeeper Server自动删除 ZNode还有一个Sequential的特性,如果创建的时候指定的话,    

        该ZNode的名字后面会自动Append一个不断增加的SequenceNo。

 Zookeeper不能用于存放大量的数据,每个节点的存放数据上限为1M

有四种类型的znode

  1. PERSISTENT-持久化目录节点客户端与zookeeper断开连接后,该节点依旧存在。
  2. PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号。
  3. EPHEMERAL-临时目录节点客户端与zookeeper断开连接后,该节点被删除。
  4. EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号。

3)版本:

在前面提到,Zookeeper 每个 ZNode 上都会存储数据,对应于每个 ZNodeZookeeper 都会为其维护一个叫作 Stat 的数据结构

Stat 中记录了这个 ZNode 的三个数据版本,分别是:

version(当前 ZNode 的版本)

cversion(当前 ZNode 子节点的版本)

 aversion(当前 ZNode ACL 版本)

4)事件监听器(watcher

ZooKeeper支持一种Watch操作,Client可以在某个ZNode上设置一个Watcher,来Watch该ZNode上的变化。如果该ZNode上有相应的变化,就会触发这个Watcher,把相应的事件通知给设置Watcher的Client。需要注意的是,ZooKeeper中的Watcher是一次性的,即触发一次就会被取消,如果想继续Watch的话,需要客户端重新设置Watcher。这个跟epoll里的oneshot模式有点类似。

5)权限控制(ACL

ZooKeeper 采用 ACLAccessControlLists)策略来进行权限控制,类似于  UNIX 文件系统的权限控制。但传统的文件系统中ACL分为两个维度,一个是属组,

       一个是权限,子目录/文件默认继承父目录的ACL。而在ZookeepernodeACL是没有继承关系的,是独立控制的。ZookeeperACL,可以从三个维度来理解:

一是scheme,二是user,三是permission,通常表示为scheme:id:permissions, 介绍如下:

  1. permission zookeeper定义了5中权限)
  2. CREATE c创建子节点的权限。解释 :可以在在当前node下创建child node的权限
  3. READ r获取节点数据和子节点列表的权限。解释 :可以获取当前node的数据,可以list当前node所有的child nodes的权限
  4. WRITE w更新节点数据的权限。解释可以向当前node写数据的权限
  5. DELETE  d删除子节点的权限。解释 :可以删除当前的node的权限
  6. ADMIN a : 设置节点ACL的权限。解释 :可以设置当前nodepermission的权限

其中尤其需要注意的是,CREATEDELETE这两种权限都是针对子节点的权限控制。刚刚搭建好的 zookeeper 单机(or 集群)没有任何权限。

[zk: localhost:2181(CONNECTED) 11] getAcl /    'world,'anyone : rwadc    #表示所有人都对这个节点有rwadc的权限

  1. schemescheme对应于采用哪种方案来进行权限管理)
  2. world : 它下面只有一个id, anyone, world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone
  3. auth : 它不需要id, 只要是通过authenticationuser都有权限(zookeeper支持通过kerberos来进行authencation, 也支持username/password形式的authentication)
  4. digest : 它对应的idusername:BASE64(SHA1(password)),它需要先通过username:password形式的authentication
  5. ip : 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如ip:192.168.1.0/16, 表示匹配前16bitIP
  6. super : 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)

       6ZK提供的API接口:

操作

解释

创建 | create

在树中的某个位置创建一个节点。例子: create /aaa bbb 创建一个aaa节点 值为bbb

删除 | delete

删除节点。例子:delete /aaa 注意若节点下没有子节点才可以删除。若存在子节点,那么先删除子节点。

获取数据 | get data

从节点读取数据。例子:get /aaa 获取节点

设定数据 | set data

将数据写入节点。例子:set /aaa bbb 1 设置aaa节点值为bbb,并且只有在版本为1的情况下生效,否则失败

存在 | exists

测试某个位置是否存在节点

得到孩子 | get children

检索节点的子节点列表

同步 | sync

等待数据传播

程序设计 传统上,ZK 应用程序需要两个单元,一个维护连接,另一个监视程序。在此应用程序中,名为 Executor 的类维护 ZK 连接。而名为DataMonitor的类 监视 ZK 树中的数据。此外,Executor包含主线程并包含执行逻辑。它负责与用户进行的少量交互,以及与作为参数传递的exectuable程序的交互,以及根据znode的状态,(根据要求)关闭并重新启动。

Executor类和DataMonitor类:

Executor:它是程序的入口。负责初始化zookeeperDataMonitor,把自己注册为DataMonitor的监听者,一旦DataMonitor监听到变化后,会通知它执行业务操作。

DataMonitor:他负责监控znode,发现znode变化后,通知listener执行业务逻辑,同时再次监控znode

5. ZK的特性

(1)、读、写(更新)模式

在ZooKeeper集群中,读可以从任意一个ZooKeeper Server读,这一点是保证ZooKeeper比较好的读性能的关键;写的请求会先Forwarder到Leader,然后由Leader来通过ZooKeeper中的原子广播协议,将请求广播给所有的Follower,Leader收到一半以上的写成功的Ack后,就认为该写成功了,就会将该写进行持久化,并告诉客户端写成功了

(2)、WAL和Snapshot

和大多数分布式系统一样,ZooKeeper也有WAL(Write-Ahead-Log),对于每一个更新操作,ZooKeeper都会先写WAL, 然后再对内存中的数据做更新,然后向Client通知更新结果。另外,ZooKeeper还会定期将内存中的目录树进行Snapshot,落地到磁盘上,这个跟HDFS中的FSImage是比较类似的。这么做的主要目的,一当然是数据的持久化,二是加快重启之后的恢复速度,如果全部通过Replay WAL的形式恢复的话,会比较慢。

  (3)、FIFO

对于每一个ZooKeeper客户端而言,所有的操作都是遵循FIFO顺序的,这一特性是由下面两个基本特性来保证的:一是ZooKeeper Client与Server之间的网络通信是基于TCP,TCP保证了Client/Server之间传输包的顺序;二是ZooKeeper Server执行客户端请求也是严格按照FIFO顺序的。

  (4)、Linearizability

在ZooKeeper中,所有的更新操作都有严格的偏序关系,更新操作都是串行执行的,这一点是保证ZooKeeper功能正确性的关键

6、Leader的工作流程

(1).首先leader开始恢复数据和清除session

启动zk实例,建立请求处理链(Leader的请求处理链):            PrepRequestProcessor->ProposalRequestProcessor-  >CommitProcessor->Leader.ToBeAppliedRequestProcessor ->FinalRequestProcessor。

(2).得到一个新的epoch,标识一个新的leader , 并获得最大zxid(方便进行数据同步)。

(3).建立一个学习者接受线程(来接受新的followers的连接,follower连接后确定followers的zxvid号,来确定是需要对follower进行什么同步措施,比如是差异同步(diff),还是截断(truncate)同步,还是快照同步)。

  (4)  . 向follower建立一个握手过程leader->follower NEWLEADER消息,并等待直到多数server发送了ack。 

  (5) . Leader不断的查看已经同步了的follower数量,如果同步数量少于半数,则回到looking状态重新进行leaderElection过程,否则继续step5。

7、Follower工作流程

(1).启动zk实例,建立请求处理链:

FollowerRequestProcessor->CommitProcessor-

>FinalProcessor。

(2).follower首先会连接leader,并将zxid和id发给

leader。

  (3) .接收NEWLEADER消息,完成握手过程。

  (4) .同leader进行状态同步。

  (5) .完成同步后,follower可以接收client

的连接。

  (6) .接收到client的请求,根据请求类型

           l         对于写操作, FollowerRequestProcessor会将该操作

           作为LEADER.REQEST发给LEADER由LEADER发起投票。

           l         对于读操作,则通过请求处理链的最后一环FinalProcessor将结果返回给客户端

            对于observer的流程不再赘述,observer流程和Follower的唯一不同的地方就是observer不会参加leader发起的投票。

8、ZK的选主流程

当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。先介绍basic paxos流程:

(1) . 选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;

(2). 选举线程首先向所有Server发起一次询问(包括自己);

  (3)  . 选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;

  (4)  . 收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次要投票的Server;

  (5)  . 线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2 + 1的Server票数, 设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。

通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1.

每个Server启动后都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信息,zk会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。选主的具体流程图如下所示:

                                   

fast paxos流程是在选举过程中,某Server首先向所有Server提议自己要成为leader,当其它Server收到提议以后,解决epoch和zxid的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息,重复这个流程,最后一定能选举出Leader。其流程图如下所示:

                                   

选完leader以后,zk就进入状态同步过程。

1. leader等待server连接;

2 .Follower连接leader,将最大的zxid发送给leader;

3 .Leader根据follower的zxid确定同步点;

4 .完成同步后通知follower 已经成为uptodate状态;

5 .Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。

流程图如下所示:

9、ZK应用场景

 (1). 名字服务(NameService)

    分布式应用中,通常需要一套完备的命令机制,既能产生唯一的标识,又方便人识别和记忆。 我们知道, 

    每个ZNode都可以由其路径唯一标识,路径本身也比较简洁直观,另外ZNode上还可以存储少量数据,这些

    都是实现统一的NameService的基础。下面以在HDFS中实现NameService为例,来说明实现NameService

    的基本布骤:

    ● 目标:通过简单的名字来访问指定的HDFS机群

    定义命名规则:这里要做到简洁易记忆。下面是一种可选的方案:

        serviceScheme://][zkCluster]-    [clusterName],比如hdfs://lgprc-example/表示基于lgprc ZooKeeper集群  

        的用来做example的HDFS集群

    配置DNS映射: 将zkCluster的标识lgprc通过DNS解析到对应的ZooKeeper集群的地址

    创建ZNode: 在对应的ZooKeeper上创建/NameService/hdfs/lgprc-example结点,将HDFS的配置文件存 

         储于该结点下

    用户程序要访问hdfs://lgprc-example/的HDFS集群,首先通过DNS找到lgprc的ZooKeeper机群的地址,

       然后在ZooKeeper的/NameService/hdfs/lgprc-example结点中读取到HDFS的配置,进而根据得到的配置,

        得到HDFS的实际访问入口

 2. 配置管理(Configuration Management)

     在分布式系统中,常会遇到这样的场景: 某个Job的很多个实例在运行,它们在运行时大多数配置项是相同 

     的,如果想要统一改某个配置,一个个实例去改,是比较低效,也是比较容易出错的方式。通过

     ZooKeeper可以很好的解决这样的问题,下面的基本的步骤:

    将公共的配置内容放到ZooKeeper中某个ZNode上,比如/service/common-conf。

    所有的实例在启动时都会传入ZooKeeper集群的入口地址,并且在运行过程中Watch /service/common- 

        conf这个ZNode。

    如果集群管理员修改了了common-conf,所有的实例都会被通知到,根据收到的通知更新自己的配置,

       并继续Watch /service/common-conf。

3. 组员管理(Group Membership)

     在典型的Master-Slave结构的分布式系统中,Master需要作为“总管”来管理所有的Slave, 当有Slave加 

     入,或者有Slave宕机,Master都需要感知到这个事情,然后作出对应的调整,以便不影响整个集群对外  

     提供服务。以HBase为例,HMaster管理了所有的RegionServer,当有新的RegionServer加入的时候,

     HMaster需要分配一些Region到该RegionServer上去,让其提供服务;当有RegionServer宕机时,

    HMaster需要将该RegionServer之前服务的Region都重新分配到当前正在提供服务的其它RegionServer上,

    以便不影响客户端的正常访问。下面是这种场景下使用ZooKeeper的基本步骤:

    Master在ZooKeeper上创建/service/slaves结点,并设置对该结点的Watcher。

    每个Slave在启动成功后,创建唯一标识自己的临时性(Ephemeral)结点/service/slaves/${slave_id},并

        将自己地址(ip/port)等相关信息写入该结点。

    Master收到有新子结点加入的通知后,做相应的处理。

    如果有Slave宕机,由于它所对应的结点是临时性结点,在它的Session超时后,ZooKeeper会自动删除

        该结点。

    Master收到有子结点消失的通知,做相应的处理。

 

4. 简单互斥锁(Simple Lock)

     我们知识,在传统的应用程序中,线程、进程的同步,都可以通过操作系统提供的机制来完成。但是在分布  

     式系统中,多个进程之间的同步,操作系统层面就无能为力了。这时候就需要像ZooKeeper这样的分布式的

     协调(Coordination)服务来协助完成同步,下面是用ZooKeeper实现简单的互斥锁的步骤,这个可以和线程间

     同步的mutex做类比来理解

     多个进程尝试去在指定的目录下去创建一个临时性(Ephemeral)结点 /locks/my_lock。

     ZooKeeper能保证,只会有一个进程成功创建该结点,创建结点成功的进程就是抢到锁的进程,假设该进

        程为A。

     其它进程都对/locks/my_lock进行Watch。

     当A进程不再需要锁,可以显式删除/locks/my_lock释放锁;或者是A进程宕机后Session超时,ZooKeeper  

        系统自动删除/locks/my_lock结点释放锁。此时,其它进程就会收到ZooKeeper的通知,并尝试去创建 

        /locks/my_lock抢锁,如此循环反复。

5. 互斥锁(Simple Lock without Herd Effect)

    上一节的例子中有一个问题,每次抢锁都会有大量的进程去竞争,会造成羊群效应(Herd Effect),为了解决

    这个问题,我们可以通过下面的步骤来改进上述过程:

    每个进程都在ZooKeeper上创建一个临时的顺序结点(Ephemeral Sequential) /locks/lock_${seq}。

    ${seq}最小的为当前的持锁者(${seq}是ZooKeeper生成的Sequenctial Number)。

    其它进程都对只watch比它次小的进程对应的结点,比如2 watch 1, 3 watch 2, 以此类推。

    当前持锁者释放锁后,比它次大的进程就会收到ZooKeeper的通知,它成为新的持锁者,如此循环反复

       这里需要补充一点,通常在分布式系统中用ZooKeeper来做Leader Election(选主)就是通过上面的机制来

       实现的,这里的持锁者就是当前的“主”。

 6. 读写锁(Read/Write Lock)

     我们知道,读写锁跟互斥锁相比不同的地方是,它分成了读和写两种模式,多个读可以并发执行,但写和

     读、写都互斥,不能同时执行行。利用ZooKeeper,在上面的基础上,稍做修改也可以实现传统的读写锁

     的语义,下面是基本的步骤:

     每个进程都在ZooKeeper上创建一个临时的顺序结点(Ephemeral Sequential) /locks/lock_${seq}。

     ${seq}最小的一个或多个结点为当前的持锁者,多个是因为多个读可以并发。

     需要写锁的进程,Watch比它次小的进程对应的结点。

     需要读锁的进程,Watch比它小的最后一个写进程对应的结点。

     ● 当前结点释放锁后,所有Watch该结点的进程都会被通知到,他们成为新的持锁者,如此循环反复。
 

7. 屏障(Barrier)

     在分布式系统中,屏障是这样一种语义: 客户端需要等待多个进程完成各自的任务,然后才能继续往前进

     行下一步。下用是用ZooKeeper来实现屏障的基本步骤:

    Client在ZooKeeper上创建屏障结点/barrier/my_barrier,并启动执行各个任务的进程。

    Client通过exist()来Watch /barrier/my_barrier结点。

    每个任务进程在完成任务后,去检查是否达到指定的条件,如果没达到就啥也不做,如果达到了就把

       /barrier/my_barrier结点删除。

    ● Client收到/barrier/my_barrier被删除的通知,屏障消失,继续下一步任务。
 

 8. 双屏障(Double Barrier)

     双屏障是这样一种语义: 它可以用来同步一个任务的开始和结束,当有足够多的进程进入屏障后,才开始

     执行任务;当所有的进程都执行完各自的任务后,屏障才撤销。下面是用ZooKeeper来实现双屏障的基 

     本步骤:

    进入屏障:

    Client Watch /barrier/ready结点, 通过判断该结点是否存在来决定是否启动任务。

    每个任务进程进入屏障时创建一个临时结点/barrier/process/${process_id},然后检查进入屏障的

       结点数是否达到指定的值,如果达到了指定的值,就创建一个/barrier/ready结点,否则继续等待。

    Client收到/barrier/ready创建的通知,就启动任务执行过程。

    离开屏障:

    Client Watch /barrier/process,如果其没有子结点,就可以认为任务执行结束,可以离开屏障。

    每个任务进程执行任务结束后,都需要删除自己对应的结点/barrier/process/${process_id}。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_270490096

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值