Zookeeper

Zookeeper是分布式系统中的中间件,负责分布式系统的协调服务。可用于

  1. master选举
  2. 实现配置文件的统一管理
  3. 实现分布式锁

Zookeeper文件系统

zk是1个类似Linux目录的文件系统,它是1个树形结构。zk的节点称为znode,znode可以存放数据,也可以挂载子节点。znode分为4类

  1. 永久节点,客户端与zk断开连接后,节点依然存在
  2. 永久顺序节点,和永久节点一样,客户端与zk断开连接后,节点依然存在,不过zk会为永久顺序节点的name加上顺序
  3. 临时节点,客户端与zk断开连接后,节点会被自动删除
  4. 临时顺序节点,和临时节点一样,断开连接后,临时节点被删除,不过zk会为它的name加上顺序

临时节点,在客户端与zk断开连接,并且心跳超时后,会被zk自动删除。这种机制可以用来实现分布式锁,应用场景:如果分布式系统中的1个进程持有锁,在它执行过程中发生异常而崩溃,这样它不会释放锁,导致所有获取锁的进程永久等待而死锁。使用临时节点,如果客户端崩溃,临时节点会被zk自动删除,避免发生死锁

节点属性包括:

  1. czxid,创建znode事务的id
  2. mzxid,修改znode事务的id
  3. znode创建时间和修改时间
  4. 节点类型
  5. cversion,节点版本号,每个节点都有自己的版本号,当节点变化时,版本号会+1
  6. dataversion,数据版本号
  7. aclversion
  8. numchildren,子节点个数

事务

zk将改变zkServer的操作,称为事务。事务包括,对节点的增删改操作,以及客户端连接Session的创建和销毁
对于每1个事务,zk都生成1个全局唯一的数值型事务id,ZXID,通过ZXID可以知道zk处理请求的先后顺序

ACL

ACL是访问控制列表,ACL用来对znode进行权限控制
ACL包括3部分:权限模式schema,授权对象和权限permission

  1. schema包括ip,digest,world和super
    ip,按照ip进行权限控制,配置以外ip的客户端不能访问节点
    digest,最常用,类似于用户名密码,zk会使用加密算法对其进行加密
    world,所有用户都能访问,类似于不设置限制
    super,超级管理员

  2. 授权对象,和权限模式schema结合使用
    (1)如果权限模式选择ip,授权对象就是ip地址
    (2)如果digest,授权对象就是用户名密码
    (3)如果world,授权对象只有1个,即anyone
    (4)如果super,授权对象就是超级管理员的用户名密码

  3. 权限,即针对节点能进行何种操作
    C D R W A
    创建 删除 读操作 写操作 管理(即对节点设置ACL的权限)

会话Session

客户端与zk通过会话Session交互,通过Session

  1. 发送心跳
  2. 发送请求,接收相应
  3. 接收Watch事件通知

值得注意的是Session的超时时间

  1. 使用Java构造Zk对象时,需要客户端配置1个Session超时时间
  2. 最终Session的超时时间由服务器端与客户端进行协商决定,服务器端默认Session最小超时时间为2个tickTime,即4s,默认Session最大超时时间为20个tickTime,即40s
  3. 当客户端指定的超时时间小于服务器端指定minTimeOut时,使用服务器端的minTimeOut作为超时时间,当大于时,使用服务器端的maxTimeOut作为超时时间

Watch

Znode发生变化,例如Znode的增删改和子节点发生变化时,可以通过Watch机制通知客户端。Watch包括一些事件,包括:

  1. 客户端与服务器端断开连接时,None
  2. 创建节点 ,NodeCreated
  3. 删除节点,NodeDeleted
  4. 节点数据变化,NodeDataChanged,本质上只关注节点的数据版本号dataVersion,当dataVersion变化时,会发送NodeDataChanged事件
  5. 节点子节点变化,NodeChildrenChanged,只关注子节点个数的变更,子节点数据变化不会触发

Watch是轻量级的,是对本地JVM中方法的回调,服务器端只存储znode是否设置了Watch,当znode发生对于Watch事件时,服务器会向客户端发送消息,客户端收到消息后触发Watch的回调方法

Leader选举

zk一共有3种角色,Leader、Follower和Observer

  1. Leader负责处理zk事务,也就是对znode的增删改操作和客户端连接的创建和销毁,发起投票
  2. Follower接收客户端的非事务操作请求并处理,向leader转发事务操作,参与投票
  3. Observer是观察者,和Follower功能类似,接收并处理非事务操作,向leader转发事务操作。和Follower不同的是,Observer不参与投票,只是为了提升系统读取速度

zk服务器一共有3种状态,Looking、Leading和Following

  1. Looking,是集群正在进行leader选举,处于无主状态
  2. Leading,集群的leader处于Leading状态
  3. Following,集群的非leader处于Following状态

Leader选举,分为2种情况:服务器启动时的选举,集群运行期间leader崩溃触发的选举

选票由2部分组成,SID和ZXID,SID即zk服务器id,也就是配置文件中的myid;ZXID为事务ID

  1. 服务器启动时的选举流程
    第1轮投票,每个Server都将选票投给自己,即SID为自己的myid,ZXID为事务ID;
    之后接收其它Server的选票并进行验证投票有效性,验证包括:检查投票是否是本轮的投票,还有投票的Server是否处于Looking状态;
    验证后进行PK,对于其它Server的投票,都与自己的投票进行PK,PK规则是:

    1. 先比较选票的ZXID,ZXID大的获胜
    2. 如果ZXID相等,比较SID,SID大的获胜
      经过多次PK之后,每个Server都会选出1个leader;
      对结果进行统计,如果有超过半数(>n/2+1)Server的投票相同,则本轮投票结果成为leader;否则继续下一轮投票,直到选出leader
      一旦Leader确定,Leader进入Leading状态,Follower进入Following状态
  2. 运行期间leader崩溃,触发leader选举
    当leader崩溃时,集群中所有节点都会将节点状态更改为Looking状态,然后进入投票
    和启动时选举一样,第1轮投票,每个Server都会将自己作为leader进行投票,经过验证,PK和统计后选出新的leader

Apache Curator

原生的ZK API有许多缺点:

  1. 超时不重连,需要手动重连
  2. Watch只能使用1次,不能多次使用
  3. 无法递归操作节点

Apache Curator解决了原生ZK的这些缺点,并且提供了一些分布式环境下的zk辅助类,例如分布式锁InterProcessMutex,类似于CountDownLatch的分布式计数器,类似于CyclicBarrier的回环栅栏等

  1. 针对原生API无法自动超时重连的情况,Curator提供了RetryPolicy重试策略接口,提供了几种重试策略。包括:
    (1)重试1次,可设置间隔时间
    (2)重试N次,可设置间隔时间
    (3)永远重试,直到连接成功
    (4)设置间隔时间,一直重试,直到累计等待时间超过阈值
    (5)设置重试次数,重试时间随机生成的策略

  2. Curator的Watch是可以重复使用的
    提供了3个类对节点进行监听,其中
    NodeCache用于监听节点自身变化
    PathChildrenCache用于监听节点的子节点变化
    TreeCache不但可以用来监听节点自身变化,还同时可以监听子节点的变化

  3. Curator可以递归的操作节点,原生API中,创建znode需要先创建其父节点,删除znode需要先删除其子节点,否则操作失败;Curator可以递归的创建和删除,使用creatingParentsIfNeeded()和deletingChildrenIfNeeded()

  1. Apache Curator提供了分布式系统中的线程同步辅助类,提供了分布式锁InterProcessMutex,分布式信号量InterProcessSemaphoreV2,分布式计数器,分布式回环栅栏等。
    以分布式锁InterProcessMutex为例
    (1)分布式系统中需要同步的所有进程,在zk锁节点下,创建子节点,这个子节点是临时有序节点。1st创建的是lock0, 2nd是lock1, 以此类推
    (2)当客户端创建的临时节点,是zk锁节点的所有子节点中最小的时,它就获取到了锁,可以对共享资源进行访问。否则,向比它小1的节点设置Watch,并阻塞等待
    (3)当客户端完成对共享资源的操作,会释放锁,释放锁就是将自己创建的临时节点删除。当临时节点被删除时,会触发比它序号大1的节点在其上面设置的Watch,对下一个节点进行通知,被通知的节点成为zk锁节点的所有子节点中最小的,获得锁,对共享资源进行访问

这里面有几个比较巧妙的做法

  1. 客户端创建的是临时节点
    创建临时节点,是为了防止客户端获取到锁,正在访问共享资源时,客户端崩溃,而未删除它创建的节点,导致所有的客户端持续等待,而死锁。
    使用临时节点,当客户端崩溃时,客户端与zk的session连接也会断开,而临时节点会在session断开时,被zk自动删除,防止死锁的发生

  2. 在获取锁的过程中,客户端会判断自己创建的节点是否是当前序号最小的,若是,则获取到锁,执行程序;否则向比它序号小1的节点设置Watch。如果客户端发现自己不是最小的,但还未来得及向比它序号小1的节点设置Watch,比它序号小1的节点就被删除,节点不存在了,无法设置Watch,而客户端创建的节点成为序号最小的,但却无法被通知,导致所有客户端持续等待而死锁。

针对这种情况,Apache Curator保证判断节点是否序号最小这个操作,和向比其序号小1节点设置Watch这个操作是原子性的,在这个两个操作中间,不会有其它的Client进行操作,比它序号小1的节点也就不会被删除

你可能感兴趣的:(Zookeeper)