搞懂ZooKeeper到底是做啥的

一.ZooKeeper是啥

ZooKeeper概念

  ZooKeeper是一个开源的分布式协调服务(a service for coordinating processes of distributed applications),由雅虎公司创建,是Google Chubby的开源实现(Google Chubby是有名的分布式锁服务,GFS和Big Table等大型系统都用它来解决分布式协调、Master选举等一系列与分布式锁服务相关的问题)。分布式程序可以基于ZooKeeper实现负载均衡,命名服务,分布式锁等功能。ZooKeeper将全量数据都存在内存中,实现提高服务器吞吐、减少延迟的目的。

  上面的英文说的足够简而意赅了,a service for coordinating processes of distributed applications。是为了协调分布式应用的,到底解决什么样的问题呢。相信大家的Java基础都不错,个人觉得还是拿锁举例子比较好理解,在单机式中,我们想保证多个线程争抢,最后只有一个线程争抢到执行权(锁)并执行你想要让他做的业务代码,最简单的方式,可以用:

synchronized (obj){
    //业务逻辑
}

  那么在分布式的情况下呢?怎么保证,同一个服务部署在不同机器上实现如上目标呢?那么这就是分布式协调服务要干的事情。分布式协调远远比同一个进程里的协调复杂得多,所以类似Zookeeper这类分布式协调服务就应运而生。在解决分布式数据一致性上,除了ZooKeeper,目前没有一个成熟稳定且被大规模应用的开源方案。且越来越多的大型分布式项目如HBase、Storm都已经使用ZooKeeper作为其核心组件,用于分布式协调。

  ZooKeeper可以保证如下分布式一致性特性:

  • 顺序一致性:从同一客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到ZooKeeper中
  • 原子性:要么整个集群所有机器都成功应用了某个事务,要么都没有应用
  • 单系统镜像:无论客户端连接的是哪个ZooKeeper服务器,其看到的服务器数据模型都是一致的(当然,ZooKeeper就是解决分布式数据一致性问题的)
  • 可靠性:一个服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一致保留下来,除非有另一个事务又对其进行了变更
  • 实时性:ZooKeeper仅保证在一定时间段内,客户端最终一定能从服务端上读取到最新的数据状态。也就是说比方说一个ZooKeeper集群,有一个时间点,数据在集群中的每个服务器不是一致的,ZooKeeper只保证最终一致性, 但是实时的一致性可以由客户端调用自己来保证,通过调用sync()方法

ZooKeeper名字诞生

  在立项初期,考虑到之前内部很多项目都是使用动物的名字来命名的(例如著名的Pig项目),雅虎的工程师希望给这个项目也取一个动物的名字。时任研究院的首席科学家 Raghu Ramakrishnan 开玩笑地说:“在这样下去,我们这儿就变成动物园了!”此话一出,大家纷纷表示就叫动物园管理员吧,因为各个以动物命名的分布式组件放在一起,雅虎的整个分布式系统看上去就像一个大型的动物园了。而 Zookeeper 正好要用来进行分布式环境的协调,于是,Zookeeper 的名字也就由此诞生了。

 

ZooKeeper的核心概念

集群角色

  在ZooKeeper中,集群有3个角色:Leader、Follower和Observer三种角色。

  Leader:ZooKeeper集群中所有机器通过选举过程选定集群中的一台机器为Leader,事务请求唯一调度者和处理者,保证集群事务处理的顺序性

  Follower:为客户端提供读服务、参与Leader选举过程、参与写操作的“过半写成功”策略,收到写事务请求直接转发给Leader

  Observer:为客户端提供读服务,在不影响集群事务处理能力的前提下提升集群的非事务处理能力

 

会话

  Session是ZooKeeper中的会话实体,代表了一个客户端会话,一个客户端连接指客户端和服务器之间的一个TCP长连接。通过这个连接,客户端能够做以下事情

  -  向ZooKeeper服务器发送请求并接收响应

  -  心跳检测

  -  接收来自服务器的Watch事件

数据节点(Znode)

  数据节点(Znode)是指数据模型中的数据单元,ZooKeeper内存数据存储的核心是DataTree,是一个树的数据结构,代表了内存中的一份完整的数据,由斜杠"/"进行分割的路径,就是一个Znode,每个Znode都保存自己的数据内容

版本

  每个Znode都有三种类型的版本信息,对节点数据变动会引起版本号变化

  version:当前数据节点数据内容的版本号

  cversion:当前数据节点子节点的版本号

  aversion:当前数据节点ACL变更版本号

Watcher

  事件监听器(Watcher)是ZooKeeper非常重要的特性,我们可以在节点上注册Watcher,并且在一些特性事件触发时候,服务器将事件通知到客户端上

ACL

  ZooKeeper使用ACL(Access Control Lists)权限控制机制保证数据安全,有5个权限:

  CREATE:创建子节点的权限

  READ:获取节点数据和子节点列表的权限

  WRITE:更新节点数据的权限

  DELETE:删除子节点的权限

  ADMIN:设置节点ACL的权限

 

二.ZooKeeper能做啥

  我们可以回头看最上面的图,Hbase,Hadoop,Kafka等已经被广泛应用在越来越多的大型分布式系统中,用来解决诸如配置管理,分布式通知/协调、集群管理和Master选举等一系列分布式问题

  ZooKeeper在阿里的实践有Dubbo,消息中间件Metamorphosis,分布式数据库同步系统Otter,实时计算引擎Jstorm等

  • 数据发布/订阅(配置中心)
  • 负载均衡
  • 分布式协调/通知
  • 集群管理
  • Master选举
  • 分布式锁

 

三.ZAB协议是啥

  可以这么说,No ZAB,No ZooKeeper。ZAB协议是整个ZooKeeper框架的核心所在。

  ZooKeeper是一个高可用的分布式数据管理与协调框架。基于对ZAB算法的实现,ZooKeeper成为了解决分布式环境中数据的一致性问题的利器。ZAB协议的全称是ZooKeeper Atomic Broadcast(ZooKeeper原子消息广播协议)。ZAB协议是一种特别为ZooKeeper设计的崩溃可恢复的原子消息广播算法。

  所有事务请求必须由唯一的Leader服务器来协调处理,其他服务器则成为Follower服务器。Leader服务器负责将事务请求转换成一个提议,并将该提议分发到集群中的所有Follower服务器,之后Leader服务器需要等待所有Follower的响应,一旦超过半数的Follower服务器进行了正确的反馈后(不需要等待集群中所有的Follower服务器都反馈响应),那么Leader就会再次向所有Follower服务器分发Commit消息,要求对前一个提议进行提交。

 

术语解释

  首先先来看一下选举算法出现的一些专有术语

SID:服务器ID

  SID是一个数据,标识一台ZooKeeper集群中的机器,SID不能重复,和myid值一样。(集群的配置文件中,server.id=host:port:port,这里的id就是myid,我们还需要在dataDir参数的目录创建myid文件,就是这里的id)

ZXID:事务ID

  ZXID是一个事务ID,标记唯一一次服务器状态的变更,某一时刻,集群中的每台机器的ZXID不一定都一致,之前已经说过了。它是一个64位的数字,低32位可以看作递增计数器,高32位代表Leader周期epoch的编号

Vote:投票

  我们可以看下Vote的数据结构:

  接下来我们来解释一下每个字段的意思:

  id:被选举的Leader的SID值

  zxid:被选举的Leader的事务ID

  electionEpoch:逻辑时钟

  peerEpoch:被选举的Leader的epoch

  state:当前服务器的状态

Quorum:过半机器数

  quorum=(n/2+1),假如集群总数是3,那么quorum就是2

 

ZAB协议三个阶段

阶段一:发现

  阶段一就是Leader选举过程(服务器启动期间或者服务器运行期间),服务器的状态进入LOCKING状态。进入选举Leader流程。

  不要死记硬背具体规则,总结简单来说,哪台服务器上的数据较新,也就是它的ZXID越大,那么越有可能成为Leader。如果几个服务器具有相同的ZXID,那么SID较大的服务器成为Leader。

  规则1:如果收到投票的ZXID大于自身的ZXID,就认可收到的投票再次投出去

  规则2:如果收到投票的ZXID小于自身的ZXID,则坚持自己的投票不做任何变更

  规则3:如果收到投票的ZXID等于自身的ZXID,则对比两者SID,如果收到投票的SID大于自身的SID则认可收到的投票再次投出去

  规则4:如果收到投票的ZXID等于自身的ZXID,并且收到投票的SID小于自身SID则坚持自己的投票不做任何变更

  接下来举个例子来说明选举的过程:

  过程A:我们假设ZooKeeper由5台机器组成,SID分别为1,2,3,4,5。ZXID分别为9,9,9,8,8。此时SID为2的机器是Leader服务器,某一时刻SID为1和2的机器出现故障,因此集群开始进行Leader选举,state切换到LOCKING状态。

  过程B:第一次投票,每台机器都选自己作为被选举的对象来进行投票,所以SID为3,4,5的投票情况为(这里Vote做简化,只有SID和ZXID):(3,9),(4,8),(5,8)。

  过程C:Server3收到(4,8)和(5,8)。根据规则2,不做任何投票的变更。

       Server4收到(3,9)和(5,8)。根据规则1,需要变更投票为(3,9)。

         Server5同样的变更投票为(3,9)。

  过程D:第二轮投票后,Server3收到超过一半的票数,成为Leader

阶段二:同步

  选举完成后,Leader服务器会为每一个Follower服务器都准备一个队列,并将那些没有被各Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器,然后在提议消息之后紧接发送一个Commit消息,表示该事务被提交,等到Follower服务器都将未同步的事务从Leader服务器同步过来并成功应用到本地数据库后,Leader服务器会将该Follower服务器加入真正可用的Follower列表中

阶段三:广播

  Leader服务器会给每个Follower分配一个FIFO的队列来分送事务,Follower服务器收到事务Proposal之后以事务日志的形式写入本地磁盘,写入成功会给Leader服务器回复一个ACK。

  当Leader服务器收到过半的ACK响应则广播发送Commit消息给所有Follower,然后所有服务器完成对事务的提交。

 

posted @ 2019-05-26 09:52  GrimMjx  阅读(3406)  评论(1编辑  收藏  举报