Zookeeper学习总结(上)——ZNode、ZAB、应用场景

1 数据结构

Znode Tree,由斜杠(/)进行分割的路径,例如/foo/path1,树状结构,类似于文件系统,但是放在内存中,而不是磁盘中。

树上的节点叫Znode,保存数据和属性,分为持久节点和临时节点。

持久节点:一旦创建,除非主动进行移除操作,否则一直保存在zk上;
临时节点:一旦客户端会话时效,那么这个客户端创建的所有临时节点都会被移除。

节点还可以设置SEQUENTIAL:创建的时候ZK会给节点后面自动加上一个整型数字,由父节点维护的自增数字。

顺序访问:ZK会为每个请求分配一个全局唯一的递增编号,该编号反映了事务操作的先后顺序。

ZK集群角色:Leader、Follower、Observer
Leader:读&写
Follower、Observer:读
Observer不参与Leader选举、也不参与“过半写成功”策略,可以在不影响写性能的情况下提升集群的读性能。

版本
Stat:version(当前Znode的版本)、cversion(子节点的版本)、aversion(ACL版本)

ACL:Access Control Lists 权限控制
5种权限:
CREATE | READ | WRITE | DELETE | ADMIN

2 分布式一致性协议

ZK采用了ZAB协议,而不是Paxos。

ZAB协议:ZooKeeper Atomic Broadcast,ZooKeepe原子广播协议。

所有事务请求都由一个Leader服务器转换成Proposal(提议),然后分发给集群中所有的Follower,一旦有超过半数的Follower进行了正确的反馈,Leader就再次向所有Follower分发Commit消息,要求提交。

2.1 崩溃恢复

什么场景下会进入崩溃恢复模式:
1.Leader服务器崩溃;
2.由于网络原因导致Leader服务器失去了与过半Follower的联系。

Leader选举:
Leader选举算法不仅需要让Leader自己知道自身已经被选举为Leader,同时还需要让集群中的其他机器也能够快速感知到选举产生的新的Leader服务器。

(1)ZAB协议需要确保那些已经在Leader服务器上提交的事务最终被所有服务器都提交;

示例:崩溃时的状态

P:Proposal

C:Commit

崩溃恢复后,P2应该被所有服务器提交。

(2)ZAB协议需要确保丢弃那些只在Leader服务器上被提出的事务。

崩溃恢复后,P3应该被所有服务器丢弃。

2.2 消息广播

类似于二阶段提交,但是移除了中断逻辑,这意味着在过半的Follower已经反馈Ack之后就可以开始提交事务Proposal了,而不需要等待集群中所有Follower都反馈。

Leader服务器会为每个事务Proposal分配一个全局递增的唯一ID,称为“事务ID”(ZXID)。

ZAB Leader选举算法选举出来的Leader服务器拥有集群中所有机器最高编号(即ZXID最大)的事务Proposal,就可以保证这个新选举出来你的Leader一定具有所有已经提交的提案。

ZXID:

一个64位的数字,其中:

1)低32位是单调递增的计数器。针对客户端的每一个事务请求,Leader产生一个新的事务Proposal,对该数字加1。

2)高32位则代表Leader周期的epoch编号。当选举产生一个新的Leader时,从该Leader服务器上取出本地日志中最大事务Proposal的ZXID,解析出epoch然后加1作为新的epoch,同时将低32位置零,生成新的ZXID。

大概是这个意思:

2.3 ZAB协议

三个阶段:发现(Leader选举,面试常问问题,后面单独介绍)、同步、广播。

消息序列:

1)CEPOCH:Follower向准Leader发送自己处理过的最后一个事务Proposal的epoch值。

2)NEWEPOCH:准Leader根据收到的各epoch生成新一轮周期的epoch值。

3)ACK-E:Follower反馈收到的NEWEPOCH。

4)NEWLEADER:准Leader确立自己的领导地位,并发送NEWLEADER给Follower。

5)ACK-LD:Follower反馈Leader发来的NEWLEADER消息。

6)COMMIT-LD:要求Follower提交历史事务Proposal。

7)PROPOSE:Leader生成针对客户端事务请求的Proposal。

8)ACK:Follower反馈Leader发过来的Proposal。

9)COMMIT:Leader发送COMMIT消息,要求所有进程提交事务PROPOSE。

 

在ZAB协议中,每个进程都可能处于以下三种状态之一:

  LOOKING:Leader选举阶段;

  FOLLOWING:Follower服务器和Leader保持同步状态;

  EADING:Leader服务器作为主进程领导状态。

所有进程启动的时候初始化状态都是LOOKING状态。

 

总结下ZK的Leader选举过程

(参考《从Paxos到Zookeeper——分布式一致性原理与实践》P74)

我的理解,重点应该是选举周期,在当前周期内的才选举,不然可能是前面选举周期内的,就放弃。

步骤1:Follower F将自己最后接受的事务Proposal的epoch值CEPOCH发送给准Leader L;

步骤2:当接到过半Follower的CEPOCH消息后,准Leader从这些消息中选出最大的epoch值,加1得到e',然后生成NEWEPOCH(e')消息给这些过半的Follower;

步骤3:当Follower接收到来自准Leader L的NEWEPOCH(e')消息后,如果检测到当前CEPOCH小于e',就将其设为e',同时向准Leader反馈Ack消息.

3 ZooKeeper典型应用场景

3.1 数据发布/订阅(Publish/Subscribe)

配置中心,例如disconf、ucc。

zk采用推(push)拉(pull)结合的方式,客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个事件通知后,需要主动到服务端获取最新的数据。

3.2 命名服务

分布式服务框架(如RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应用根据指定的名字获取资源实体、服务地址和提供者信息等。

如何生成分布式全局唯一ID?

方案1:用数据库自增主键——不可行,分表后不再唯一。

方案2:UUID——也不可行,32位字符+4个断线的字符串,长度过长,而且含义不明。

方案3:ZooKeeper可以实现:利用ZK可以创建顺序节点的特性,每次create节点会自动加上序号:

3.3 分布式日志收集

利用zk两大特性:

  • 客户端如果对zk的一个数据节点注册Watcher监听,那么当该数据节点的内容或其子节点列表发生变更时,zk服务器就会向订阅的客户端发送变更通知。
  • 对在zk上创建的临时节点,一旦客户端与服务器之间的会话失效,那么该节点也就被自动清除。

应用场景:分布式环境中,日志源机器和日志收集机器都经常变化,比如机器故障、扩容、迁移或者网络问题。

1)注册收集器

/logs/collector/[Hostname] 是收集器节点,status记录收集器状态,是一种心跳检测机制。

2)任务分发

给收集器分配日志源机器列表。

3)状态汇报

收集器把自己的状态放在:/logs/collector/host1/status。

收集器节点是永久节点,是为了防止一旦收集器崩溃了,分给它的日志源机器列表也丢失了,所以需要用status标识存活状态。

4)动态分配

一旦收集器机器挂掉了或者扩容了,就要动态分配日志收集任务。

3.4 Master选举

注意分清zk自己的选举和给别人选举。前面讨论的master选举,是在zk自己所在的集群中选出一台服务器成为master,这里的选举是zk给别人提供选举服务,从别人的系统里选出一个master。

一个不可行的办法:利用关系型数据库的主键特性保证在集群中选出一个唯一的Master,怎么做的呢?集群中所有机器都向数据库中插入一条相同主键ID的记录,谁抢先写进去谁就是master。

不可行的原因:如果当前选举出来的master挂了,谁来告诉我master挂了?显然关系型数据库没法通知我们。

zk登场:利用了zk的强一致性:zk将会保证客户端无法重复创建一个已经存在的数据节点。(这跟MySQL的primary key保证唯一性有啥区别?高在哪里?别急,我一开始也是这么吐槽的,往下看,绝不是这么简单)

客户端集群每天定时往zk里创建一个临时节点:/master_election/2013-09-20/binding,在这个过程中,只有一个客户端能够创建成功,这个客户端就成了master了,其他没有创建成功的客户端统统都要在节点/master_election/2013-09-20上注册一个子节点变更的Watcher,一旦master挂了就会通知这些监听着的客户端,这些客户端就会发起新的master选举。

这个就高明了很多了,可以利用监听机制实现动态选举,妈妈再也不用担心我会挂掉了。

3.5 分布式锁

3.5.1 排他锁(Exclusive Locks,简称X锁)

又称写锁或独占锁。如果事务T1对对象O1加了排他锁,允许T1对O1读取和更新,其他任何事务都不能对O1进行任何操作。

zk实现:临时节点 + 监听

1)获取锁:客户端调用create()接口,在/exclusive_lock节点下创建临时节点/exclusive_lock/lock,zk会保证最终只有一个客户端能创建成功,那些创建失败的客户端会在/exclusive_lock节点上注册子节点变更监听Watcher。

2)释放锁:

  • 客户端连接异常断开了(宕机了、断网了。。)
  • 客户端执行完业务自己主动删除该临时节点

3.5.2 共享锁(Shared Locks,简称S锁)

如果事务T1对对象O1加了共享锁,T1只能对O1进行读取操作,其他事务也只能加共享锁。

所有事务可以同时读取,但只能有一个可以更新。

zk实现:临时顺序节点 + 监听

1)获取锁:在/shared_lock节点下面创建临时顺序节点。

R-读锁;W-写锁

2)读写顺序

读请求:如果没有比自己序号小的子节点,或者比自己小的子节点都是读请求,那么表明自己已经成功获取到了共享锁,可以开始执行读取逻辑;如果比自己序号小的节点中有写请求,就要进入等待。

写请求:自己不是序号最小的子节点就要进入等待

3.6 分布式队列

两种,一种FIFO(先进先出)的,一种Barrier(屏障)。

FIFO:创建临时顺序节点,相当于一个全写的共享锁模型,创建节点的时候,如果自己不是序号最小的节点,就在比自己小的最后一个节点上创建一个Watcher监听。

Barrier:屏障,栅栏,jdk里面是叫CyclierBarrier,还有个类似功能的CountDownLatch,不赘述。实现就是给节点的数据内容赋值n,表示这个节点下最多能创建n个子节点,然后在该节点下创建临时节点,每次创建的时候统计子节点个数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值