面试阿里,腾讯90%会被问到的zookeeper,把这篇文章看完就够了。

Zookeeper概述

zookeeper高容错数据一致性协议(CP)的分布式小文件系统,提供相似于文件系统的目录方式的数据存储。node

  1. 全局数据一致性:每一个server保存一份相同的数据副本,client不管链接到哪一个server展现的数据都是一致的。
  2. 可靠性:一旦事务成功提交,就会被保留下来。
  3. 有序性:客户端发起的事务请求,在也会顺序的应用在Zookeeper中。
  4. 数据更新原子性:一次数据更新要么成功要么失败,不存在中间状态。
  5. 实时性:保证客户端在一个间隔时间范围内获取服务的更新消息或服务器失效信息。

zookeeper单机

数据模型:

每个节点都是znode(兼具文件和目录两种特色),每个znode都具备原子操做。znode存储的数据大小有限制(默认1MB),经过绝对路径引用。 znode分为3个部分:数据库

  • stat:状态信息,描述znode的版本和权限等信息。
  • data:与该znode关联的数据。
  • children:该znode下的子节点。

znode节点类型

  1. 临时节点:该节点的生命周期依赖于建立它的会话,一旦会话结束临时节点就会被删除。临时节点不容许拥有子节点。
  2. 永久节点:只能经过客户端显示执行删除操做。
  3. 临时序列化节点。
  4. 永久序列化节点。

znode序列化:znode的名字后面追加一个不断增长的序列号。每个序列号对父节点来讲是惟一的,能够记录每个子节点的前后顺序。缓存

znode节点属性

zookeeper的节点属性包括节点数据,状态,权限等信息。安全

属性 说明
cZxid znode建立节点的事务ID,Zookeeper中每一个变化都会产生一个全局惟一的zxid。经过它可肯定更新操做的前后顺序
ctime 建立时间
mZxid 修改节点的事务ID
mtime 最后修改时间
pZxid 子节点变动的事务ID,添加子节点或删除子节点就会影响子节点列表,可是修改子节点的数据内容则不影响该ID
cversion 子节点版本号,子节点每次修改版本号加1
dataversion 数据版本号,数据每次修改该版本号加1 ,多个客户端对同一个znode进行更新操做时,由于数据版本号,才能保证更新操做的前后顺序性。例:客户端A正在对znode节点作更新操做,此时若是另外一个客户端B同时更新了这个znode,则A的版本号已通过期,那么A调用setData不会成功。
aclversion 权限版本号,权限每次修改该版本号加1
dataLength 该节点的数据长度
numChildern 该节点拥有的子节点的数量

znode ACL权限控制

ACL权限控制使用 schema:id:permission来标识。 示例:setAcl /test2 ip:128.0.0.1:crwda服务器

Schema

Schema枚举值 说明
world 使用用户名设置,id为一个用户,但这个id只有一个值:anyone,表明全部人
ip 使用IP地址进行认证,ID对应为一个IP地址或IP段
auth 使用已添加认证的用户进行认证,以经过addauth digest user:pwd 来添加当前上下文中的受权用户
digest 使用“用户名:密码”方式认证

permission

权限 ACL简写 描述
CREATE c 能够建立子节点
DELETE d 能够删除子节点(仅下一级节点)
READ r 能够读取节点数据及显示子节点列表
WRITE w 能够设置节点数据
ADMIN a 能够设置节点访问控制列表权限

权限相关命令

命令 使用方式 描述
getAcl getAcl 读取ACL权限
setAcl setAcl 设置ACL权限
addauth addauth 添加认证用户

zookeeper:提供了分布式发布/订阅功能,能让多个订阅者同时监听某一个主题对象,经过watche机制来实现。网络

zookeeper watch机制

一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。session

  • 父节点的建立,修改,删除都会触发Watcher事件。
  • 子节点的建立,删除会触发Watcher事件。

监听器watch特性

特性 说明
一次性 Watcher是一次性的,一旦被触发就会移除,再次使用时须要从新注册。监听的客户端不少状况下,每次变更都要通知到全部的客户端,给网络和服务器形成很大压力。一次性能够减轻这种压力
客户端顺序回调 客户端 Watcher 回调的过程是一个串行同步的过程。
轻量 Watcher 通知很是简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。

监听器原理

  1. 首先要有一个main()线程,在main线程中建立Zookeeper客户端,这时就会建立两个线程,一个负责网络链接通讯(connet),一个负责监听(listener)。
  2. 经过connect线程将注册的监听事件发送给Zookeeper服务端。
  3. 在Zookeeper服务端的注册监听器列表中将注册的监听事件添加到列表中。
  4. Zookeeper监听到有数据或路径变化,就会将这个消息发送

给listener线程。数据结构

  1. listener线程内部调用了process()方法来触发Watcher。

zookeeper会话管理

  1. 客户端会不时地向所链接的ZkServer发送ping消息,ZkServer接收到ping消息,或者任何其它消息的时候,都会将客户端的session_id,session_timeout记录在一个map中。
  2. Leader ZkServer会周期性地向全部的follower发送心跳消息,follower接收到ping消息后,会将记录session信息的map做为返回消息,返回给leader,同时清空follower本地的map。 Leader使用这些信息从新计算客户端的超时时间。
  3. 一旦在session timout的时间到,leader即没有从其它follower上收集到客户端的session信息,也没有直接接收到该客户端的任何请求,那么该客户端的session就会被关闭。

zookeeper数据模型

  1. zk维护的数据主要有:客户端的会话(session)状态及数据节(dataNode)信息。
  2. zk在内存中构造了个DataTree的数据结构,维护着path到dataNode的映射以及dataNode间的树状层级关系。为了提升读取性能,集群中每一个服务节点都是将数据全量存储在内存中。因此,zk最适于读多写少且轻量级数据的应用场景。

3.数据仅存储在内存是很不安全的,zk采用事务日志文件及快照文件的方案来落盘数据,保障数据在不丢失的状况下能快速恢复。分布式

Zookeeper集群

集群角色

  1. Leader:集群工做的核心,事务请求的惟一调度和处理者,保证事务处理的顺序性。对于有写操做的请求,需统一转发给Leader处理。Leader需决定编号执行操做。
  2. Follower:处理客户端非事务请求,转发事务请求转发给Leader,参与Leader选举。
  3. Observer观察者:进行非事务请求的独立处理,对于事务请求,则转发给Leader服务器进行处理.不参与投票。

事务

  1. 事务:ZooKeeper中,能改变ZooKeeper服务器状态的操做称为事务操做。通常包括数据节点建立与删除、数据内容更新和客户端会话建立与失效等操做。对应每个事务请求,ZooKeeper 都会为其分配一个全局惟一的事务ID,用 ZXID 表示,一般是一个64位的数字。每个ZXID对应一次更新操做,从这些ZXID中能够间接地识别出ZooKeeper处理这些事务操做请求的全局顺序。
  2. 事务日志:全部事务操做都是须要记录到日志文件中的,可经过 dataLogDir配置文件目录,文件是以写入的第一条事务zxid为后缀,方便后续的定位查找。zk会采起“磁盘空间预分配”的策略,来避免磁盘Seek频率,提高zk服务器对事务请求的影响能力。默认设置下,每次事务日志写入操做都会实时刷入磁盘,也能够设置成非实时(写到内存文件流,定时批量写入磁盘),但那样断电时会带来丢失数据的风险。事务日志记录的次数达到必定数量后,就会将内存数据库序列化一次,使其持久化保存到磁盘上,序列化后的文件称为"快照文件"。有了事务日志和快照,就可让任意节点恢复到任意时间点

性能

  1. 数据快照:数据快照是zk数据存储中另外一个很是核心的运行机制。数据快照用来记录zk服务器上某一时刻的全量内存数据内容,并将其写入到指定的磁盘文件中,可经过dataDir配置文件目录。可配置参数snapCount,设置两次快照之间的事务操做个数,zk节点记录完事务日志时,会统计判断是否须要作数据快照(距离上次快照,事务操做次数等于[snapCount/2~snapCount] 中的某个值时,会触发快照生成操做,随机值是为了不全部节点同时生成快照,致使集群影响缓慢)。

过半原则

1. 过半:所谓“过半”是指大于集群机器数量的一半,即大于或等于(n/2+1),此处的“集群机器数量”不包括observer角色节点。leader广播一个事务消息后,当收到半数以上的ack信息时,就认为集群中全部节点都收到了消息,而后leader就不须要再等待剩余节点的ack,直接广播commit消息,提交事务。半数选举致使zookeeper一般由2n+1台server组成。

  1. zookeeper的两阶段提交:zookeeper中,客户端会随机链接到 zookeeper 集群中的一个节点,若是是读请求,就直接从当前节点中读取数据。若是是写请求,那么请求会被转发给 leader 提交事务,而后 leader 会广播事务,只要有超过半数节点写入成功,那么写请求就会被提交。

    1. Leader将写请求转化为一个Proposal(提议),将其分发给集群中的全部Follower节点。
    2. Leader等待全部的Follower节点的反馈,一旦超过半数Follower进行了正确的反馈,那么Leader就会再次向全部的Follower节点发送Commit消息,要求各个Follower节点对前面的一个Proposal节点进行提交。
    3. leader节点将最新数据同步给Observer节点。
    4. 返回给客户端执行的结果。

ZAB协议

ZooKeeper 可以保证数据一致性主要依赖于 ZAB 协议的消息广播崩溃恢复数据同步三个过程。

消息广播

  1. 一个事务请求进来以后,Leader节点会将写请求包装成提议(Proposal)事务,并添加一个全局惟一的 64 位递增事务 ID,Zxid。
  2. Leader 节点向集群中其余节点广播Proposal事务,Leader 节点和 Follower 节点是解耦的,通讯都会通过一个 FIFO 的消息队列,Leader 会为每个 Follower 节点分配一个单独的 FIFO 队列,而后把 Proposal 发送到队列中。
  3. Follower 节点收到对应的Proposal以后会把它持久到磁盘上,当彻底写入以后,发一个ACK给Leader。
  4. 当Leader节点收到超过半数Follower节点的ACK以后会提交本地机器上的事务,同时开始广播commit,Follower节点收到 commit 以后,完成各自的事务提交。

消息广播相似一个分布式事务的两阶段提交模式。在这种模式下,没法处理因Leader在发起事务请求后节点宕机带来的数据不一致问题。所以ZAB协议引入了崩溃恢复机制。

崩溃恢复

当整个集群在启动时,或者Leader失联后,ZAB协议就会进入恢复模式,恢复模式的流程以下:

  1. 集群经过过民主举机制产生新的Leader,纪元号加1,开始新纪元
  2. 其余节点重新的Leader同步状态
  3. 过半节点完成状态同步,退出恢复模式,进入消息广播模式

Leader选举流程

server工做状态

状态 说明
LOOKING 竞选状态,当服务器处于该状态时,它会认为当前集群中没有 Leader,所以须要进入 Leader 选举状态。
FOLLOWING 跟随者状态。代表当前服务器角色是 Follower。它负责从Leader同步状态,并参与选举投票。
LEADING 领导者状态。代表当前服务器角色是 Leader。
OBSERVING 观察者状态,代表当前服务器角色是Observer,它负责从同步leader状态,不参与投票。

选举原则

  1. 选举投票必须在同一轮次中进行,若是Follower服务选举轮次不一样,不会采纳投票。
  2. 数据最新的节点优先成为Leader,数据的新旧使用事务ID断定,事务ID越大认为节点数据约接近Leader的数据,天然应该成为Leader。
  3. 若是每一个个参与竞选节点事务ID同样,再使用server.id作比较。server.id是节点在集群中惟一的id,myid文件中配置。

选举阶段

集群间互传的消息称为投票,投票Vote主要包括二个维度的信息:ID、ZXID  ID   候选者的服务器ID ZXID 候选者的事务ID,从机器DataTree内存中获取,确保事务已经在机器上被commit过。

选主过程当中主要有三个线程在工做

  1. 选举线程:主动调用lookForLeader方法的线程,经过阻塞队sendqueue及recvqueue与其它两个线程协做。
  2. WorkerReceiver线程:选票接收器,不断获取其它服务器发来的选举消息,筛选后会保存到recvqueue队列中。zk服务器启动时,开始正常工做,不中止
  3. WorkerSender线程:选票发送器,会不断地从sendqueue队列中获取待发送的选票,并广播至集群。
  4. WorkerReceiver线程一直在工做,即便当前节点处于LEADING或者FOLLOWING状态,它起到了一个过滤的做用,当前节点为LOOKING时,才会将外部投票信息转交给选举线程处理;
  5. 若是当前节点处于非LOOKING状态,收到了处于LOOKING状态的节点投票数据(外部节点重启或网络抖动状况下),说明发起投票的节点数据跟集群不一致,这时,当前节点须要向集群广播出最新的内存Vote(id,zxid),落后的节点收到该Vote后,会及时注册到leader上,并完成数据同步,跟上集群节奏,提供正常服务。

全新集群选举

  1. 每一机器都给本身一票。
  2. 主要服务器ID的值,值越大选举权重越大。
  3. 投票数过半,选举结束。

非全新集群选举

  1. 逻辑时钟:逻辑时钟小的选举结果被忽略
  2. 数据ID:数据ID大的胜出
  3. 服务ID:数据ID相同,服务器ID大的胜出,被选举为leader。

选举过程详细说明

Leader选举是集群正常运行的前提,当集群启动或Leader失联后,就会进入Leader选举流程。

  1. 全部节点进入LOOKING状态
  2. 每一个节点广播携带自身ID和ZXID的选票,投票推举本身为Leader
  3. 节点接收其余节点发送的选票,把选票信息和本身推举的选票进行PK(选票中ZXID大者胜出,ZXID相同,则ID大者胜出)
  4. 若是外部选票获胜,则保存此选票信息,并把选票广播出去(同意该选票)
  5. 循环上述3-4步骤
  6. 当有选票获得超过半数节点同意,且该选票的全部者也同意该选票,则选举成功,该选票全部者成为Leader
  7. Leader切换为LEADING,Follower切换为FOLLOWING,Observer切换为OBSERVING状态,选举结束,进入数据同步流程。

数据同步流程

数据同步流程,是要以Leader数据为基础,让集群数据达到一致状态。

  1. 新Leader把本地快照加载到内存,并经过日志应用快照以后的全部事务,确保Leader数据库是最新的。
  2. Follower和Observer把自身的ZXID和Leader的ZXID进行比较,肯定每一个节点的同步策略
  3. 根据同步策略,Leader把数据同步到各节点
  4. 每一个节点同步结束后,Leader向节点发送NEWLEADER指令
  5. 同步完成的Follower节点返回ACK
  6. 当Leader收到过半节点反馈的ACK时,认为同步完成

Leader向Follower节点发送UPTODATE指令,通知集群同步完成,开始对外服务。

zk应用举例

  1. 命名服务:经过使用命名服务,客户端应用可以根据指定名字来获取资源或服务的地址,提供者等信息。经过建立全局惟一的path做为一个名字。
  2. 分布式锁:独占锁,获取数据以前要求全部的应用去zk集群的指定目录去建立一个临时非序列化的节点。谁建立成功谁就能得到锁,操做完成后断开节点。其它应用若是须要操做这个文件就可去监听这个目录是否存在。
  3. 控制时序:经过建立一个临时序列化节点来控制时序性。
  4. 心跳检测:让不一样的进程都在ZK的一个指定节点下建立临时子节点,不一样的进程直接能够根据这个临时子节点来判断对应的进程是否存活。大大减小了系统耦合。
  5. master选举:每一个客户端请求建立同一个临时节点,那么最终必定只有一个客户端请求可以建立成功。利用这个特性,就能很容易地在分布式环境中进行 Master 选举了。成功建立该节点的客户端所在的机器就成为了Master。同时,其余没有成功建立该节点的客户端,都会在该节点上注册一个子节点变动的 Watcher,用于监控当前 Master 机器是否存活,一旦发现当前的Master挂了,那么其余客户端将会从新进行 Master 选举。

zookeeper缺点:

1. 非高可用:极端状况下zk会丢弃一些请求:机房之间链接出现故障。

  1. zookeeper master就只能照顾一个机房,其余机房运行的业务模块因为没有master都只能停掉,对网络抖动很是敏感。
  2. 选举过程速度很慢且zk选举期间没法对外提供服务。
  3. zk的性能有限:典型的zookeeper的tps大概是一万多,没法覆盖系统内部天天动辄几十亿次的调用。所以每次请求都去zookeeper获取业务系统master信息是不可能的。所以zookeeper的client必须本身缓存业务系统的master地址。
  4. zk自己的权限控制很是薄弱.
  5. 羊群效应: 全部的客户端都尝试对一个临时节点去加锁,当一个锁被占有的时候,其余的客户端都会监听这个临时节点。一旦锁被释放,Zookeeper反向通知添加监听的客户端,而后大量的客户端都尝试去对同一个临时节点建立锁,最后也只有一个客户端能得到锁,可是大量的请求形成了很大的网络开销,加剧了网络的负载,影响Zookeeper的性能.

 * 解决方法:是获取锁时建立一个临时顺序节点,顺序最小的那个才能获取到锁,以后尝试加锁的客户端就监听本身的上一个顺序节点,当上一个顺序节点释放锁以后,本身尝试加锁,其他的客户端都对上一个临时顺序节点监听,不会一窝蜂的去尝试给同一个节点加锁致使羊群效应。

  1. zk进行读取操做,读取到的数据多是过时的旧数据,不是最新的数据。若是一个zk集群有10000台节点,当进行写入的时候,若是已经有6K个节点写入成功,zk就认为本次写请求成功。可是这时候若是一个客户端读取的恰好是另外4K个节点的数据,那么读取到的就是旧的过时数据。

zookeeper脑裂:

假死:因为心跳超时(网络缘由致使的)认为leader死了,但其实leader还存活着。

脑裂

因为假死会发起新的leader选举,选举出一个新的leader,但旧的leader网络又通了,致使出现了两个leader ,有的客户端链接到老的leader,而有的客户端则链接到新的leader。

quorum(半数机制)机制解决脑裂

在zookeeper中Quorums有3个做用:

  1. 集群中最少的节点数用来选举leader保证集群可用。
  2. 通知客户端数据已经安全保存前集群中最少数量的节点数已经保存了该数据。一旦这些节点保存了该数据,客户端将被通知已经安全保存了,能够继续其余任务。而集群中剩余的节点将会最终也保存了该数据。
  3. 假设某个leader假死,其他的followers选举出了一个新的leader。这时,旧的leader复活而且仍然认为本身是leader,这个时候它向其余followers发出写请求也是会被拒绝的。由于每当新leader产生时,会生成一个epoch标号(标识当前属于那个leader的统治时期),这个epoch是递增的,followers若是确认了新的leader存在,知道其epoch,就会拒绝epoch小于现任leader epoch的全部请求。那有没有follower不知道新的leader存在呢,有可能,但确定不是大多数,不然新leader没法产生。Zookeeper的写也遵循quorum机制,所以,得不到大多数支持的写是无效的,旧leader即便各类认为本身是leader,依然没有什么做用。
相关文章
相关标签/搜索