ZKEssentials读书笔记(四) - 18965050/ZookeeperEssentials GitHub Wiki


  • ZK Recipes

屏障(Barrier)

barrier是一种同步状态. 指的是阻塞分布式进程直到达到一定数量后才可以继续进行. ZK实现:

  • 设置一路障节点, 比如/zk_barrier
  • 启用barrier时, 屏障节点(/zk_barrier)必须存在
  • client调用带watch的exists()方法
  • 如果返回false,说明屏障消失, 可以进行进行业务处理
  • 如果返回true, 说明屏障存在, 进行等待
  • 当到达屏障离开状态(exit condition),删除节点/zk_barrier
  • watch被调用, 再次通过exist()返回true或false来判断是否可以继续进行

双重屏障(Double Barrier)

双重屏障是一种特殊的屏障,其用来同步一个任务的开始和结束. 当有足够多的进程进入屏障后,才开始执行任务;当所有的进程都执行完各自的任务后,屏障才撤销。 ZK实现:

  • 加入屏障

    • 每个client在指定的znode(比如/barrier)下创建临时子目录(ephemeral znode),并添加watch

    • 一个负责监听(watch)的子节点监听/barrier子目录数量, 如果达到预定的N,则创建ready子节点

    • /barrier子节点发生变化, 触发watch调用,通过

       M = getChildren(/barrier, watch=false)
      

      可获取到M数量, 如果比预定数量N小, 继续屏蔽

    • 当M==N时,负责监听的子节点在/barrier下创建ready子目录, 并触发加入到barrier的其他节点判断/barrier下是否存在子目录ready

  • 离开屏障

    • 完成的子节点在/barrier目录下删除自己创建出来的子目录

    • 通过调用:

       M = getChildren(/barrier, watch=True)
      

      获取M数量

    • 如果M!=0,屏障存在;M==0,屏障结束

上述的流程有个很大的问题, 即羊群效应(herd effect). 这可以通过创建临时有序目录(ephemeral sequential)来解决. 这样每一个节点只要watch其前面的一个子目录, 而不是父目录就可以了

队列(Queue)

分布式队列遵循生产者-消费者模型,并严格按照FIFO的顺序执行. 这在ZK中可以通过有序znode来实现. ZK实现:

  • 创建/queue作为父目录

  • 生成者调用

     ZooKeeper.create( "queue-", SEQUENCE_EPHEMERAL)
    

    创建有序子目录

  • client通过get_children()方法,并找出最小序号目录进行处理, 处理结束删除此目录

  • 需要注意的是client 删除子目录可能不成功.这是由于被其他client获得了处理权. 这需要再次delete

锁(Lock)

ZK实现如下:

  • 获取锁
    • 调用 create("/_locknode_/lock-",CreateMode=EPHEMERAL_SEQUENTIAL)
    • 需要获取锁的节点应用调用 getChildren("/_locknode_/lock-", false), 注意watch为false
    • 如果创建目录拥有最小的序号后缀(0000000001),则获取锁
    • 否则调用 exists("/_locknode_/<znode path with next lowest sequence number>, True) .如果返回true,则获取锁;返回false,未获取锁
  • 释放锁
    • 获得锁的节点删除子目录, 触发下一个client获得锁
    • 下一个触发的client是序号第二小的节点

领导选举(Leader Election)

还来利用ephemeral sequential znode来实现.序号后缀最小的为leader,其他为follower. 当leader崩溃后, 序号后缀第二小的为leader, 其他为follower

成员管理(Membership)

利用ephemeral znode来实现. 这个比较简单, 不细说了

两阶段提交(Two-phase Commit)

两阶段提交包括两个阶段

  • 阶段一: 协调节点(coordinator node)向事务(transaction)的所有参与方节点发出投票请求,事务提交或事务回滚
  • 阶段二: 如果事务所有投票节点投票事务提交, 则协调节点决定事务提交. 只要有一个投票节点事务回滚, 则协调节点决定事务回滚

ZK实现:

  • 创建一根目录(比如 /2PC_Transactions). 协调节点创建/2PC_Transactions/TX 作为事务目录,并在此目录下创建 tx_result 子目录作为最终的事务结果
  • 事务参与节点对/2PC_Transactions/TX/tx_result 节点进行监听
  • 协调节点通知事务参与节点进行事务投票
  • 事务参与节点在/2PC_Transactions/TX 目录下创建临时znode,并写入事务提交或事务回滚数据
  • 协调节点收到/2PC_Transactions/TX 子目录变化通知.并判断数量是否和应有的事务参与节点数量一致
  • 一致情况下,分析每个临时子目录数据, 来判断是否事务提交或回滚
  • 当所有临时子目录数据为提交时,整个事务提交. 否则事务回滚. 将最终数据写入/2PC_Transactions/TX/tx_result znode中
  • 事务参与节点监听到数据变化后, 得知事务是否最终提交或回滚

服务发现(Service Discovery)

通过对子目录变化监听, 可比较简单实现此功能