一、Zookeeper 实现机制


文件系统 + 通知机制

二、Zookeeper 高可用实现原理


一个 ZooKeeper 集群如果要对外提供可用的服务,那么集群中必须要有过半的机器正常工作并且彼此之间能够正常通信。如果想搭建一个能够允许 N 台机器 down 掉的集群,那么就要部署一个由 2*N+1 台服务器构成的 ZooKeeper 集群。所以部署3个节点,那么就得至少有2个节点可用则该集群才可用。4个节点同样还是要2个以上。所以 Zookeeper集群部署的节点(非Observer)数一般为奇数。高可用机制其实基于 ZAB协议[链接]

三、Zookeeper文件系统


Zookeeper 提供一个多层级的节点命名空间(节点称为znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。Zookeeper为了保证高吞吐低延迟,在内存中维护了这个树状的目录结构,这种特性使得 Zookeeper不能用于存放大量的数据,每个节点的存放数据上限为1M。

四、四种类型的数据节点 Znode


【1】PERSISTENT 持久节点:除非手动删除,否则节点一直存在于 Zookeeper上;
【2】EPHEMERAL 临时节点:临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与 Zookeeper连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除;
【3】PERSISTENT_SEQUENTIAL 持久顺序节点:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字;
【4】EPHEMERAL_SEQUENTIAL 临时顺序节点:基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字;

五、Zookeeper Watcher 机制


Zookeeper 客户端向服务端的某个 Znode注册一个 Watcher监听,当服务端对 Znode节点做指定事件[修改,删除]时,触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher通知状态和事件类型做出业务上的改变。

Watcher 特性总结:
【1】一次性:无论是服务端还是客户端,一旦一个 Watcher被触发,Zookeeper都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。
【2】客户端串行执行:客户端 Watcher回调的过程是一个串行同步的过程。
【3】轻量:Watcher通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。客户端向服务端注册 Watcher的时候,并不会把客户端真实的 Watcher对象实体传递到服务端,仅仅是在客户端请求中使用 boolean类型属性进行了标记。
【4】watcher event异步发送:watcher的通知事件从
server发送到 client是异步的,这就存在一个问题,不同的客户端和服务器之间通过
socket进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于 Zookeeper本身提供了ordering
guarantee,即客户端监听事件后,才会感知它所监视 znode发生了变化。所以我们使用
Zookeeper不能期望能够监控到节点每次的变化。Zookeeper只能保证最终的一致性,而无法保证强一致性。
【5】注册 watcher:getData、exists、getChildren。
【6】触发 watcher:create、delete、setData。
【7】当一个客户端连接到一个新的服务器上时,watch将会被以任意会话事件触发:当与一个服务器失去连接的时候,是无法接收到
watch的。而当 client重新连接时,如果需要的话,所有先前注册过的
watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch可能会丢失:对于一个未创建的 znode的exist
watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个 watch事件可能会被丢失。

六、客户端注册 Watcher实现


【1】调用 getData()/getChildren()/exist() 三个API,传入Watcher对象;
【2】标记请求 request,封装 Watcher到 WatchRegistration;
【3】封装成 Packet对象,发服务端发送 request;
【4】收到服务端响应后,将 Watcher注册到 ZKWatcherManager中进行管理;
【5】请求返回,完成注册;

七、服务端处理 Watcher实现


【1】服务端接收 Watcher并存储:接收到客户端请求,处理请求判断是否需要注册Watcher,需要的话将数据节点的节点路径和 ServerCnxn(ServerCnxn代表一个客户端和服务端的连接,实现了 Watcher的 process接口,此时可以看成一个 Watcher对象)存储在 WatcherManager的 WatchTable和 watch2Paths中去。
【2】Watcher 触发:以服务端接收到 setData() 事务请求触发NodeDataChanged事件为例:
    ● 封装 WatchedEvent:将通知状态(SyncConnected)、事件类型(NodeDataChanged)以及节点路径封装成一个 WatchedEvent对象;
    ● 查询 Watcher:从 WatchTable中根据节点路径查找 Watcher;
    ● 没找到,说明没有客户端在该数据节点上注册过 Watcher;
    ● 找到,提取并从 WatchTable和 Watch2Paths中删除对应Watcher(从这里可以看出Watcher在服务端是一次性的,触发一次就失效了);
【3】调用 process方法来触发Watcher:这里 process主要就是通过 ServerCnxn对应的 TCP连接发送 Watcher事件通知。

八、客户端回调 Watcher


客户端 SendThread线程接收事件通知,交由 EventThread线程回调 Watcher。客户端的 Watcher机制同样是一次性的,一旦被触发后,该 Watcher就失效了。

九、ZAB协议


ZAB协议是为分布式协调服务 Zookeeper专门设计的一种支持崩溃恢复原子广播协议。当整个 zookeeper集群刚刚启动或者 Leader服务器宕机、重启或者网络故障导致不存在过半的服务器与 Leader服务器保持正常通信时,所有进程(服务器)进入崩溃恢复模式,首先选举产生新的 Leader服务器,然后集群中 Follower服务器开始与新的 Leader服务器进行数据同步,当集群中超过半数机器与该 Leader服务器完成数据同步之后,退出恢复模式进入消息广播模式,Leader服务器开始接收客户端的事务请求生成事物提案来进行事务请求处理。ZAB协议[链接]

十、Leader 选举


详细博文链接

十一、数据同步


整个集群完成 Leader选举后,Learner(Follower和Observer的统称)会向 Leader服务器进行注册。当 Learner服务器向 Leader服务器完成注册后,进入数据同步环节。

Zookeeper 的数据同步通常分为四类
【1】直接差异化同步(DIFF同步)
【2】先回滚再差异化同步(TRUNC+DIFF同步)
【3】仅回滚同步(TRUNC同步)
【4】全量同步(SNAP同步)

进行数据同步前,Leader会完成数据同步初始化:
【1】peerLastZxid:Learner服务器注册时发送的 ACKEPOCH消息中提取 lastZxid(该Learner服务器最后处理的ZXID)
【2】minCommittedLog:Leader服务器 Proposal缓存队列 committedLog中最小ZXID;
【3】maxCommittedLog:Leader服务器 Proposal缓存队列 committedLog中最大ZXID;

直接差异化同步(DIFF同步):peerLastZxid介于 minCommittedLog 和 maxCommittedLog之间;

先回滚再差异化同步(TRUNC+DIFF同步):当新的 Leader服务器发现某个 Learner服务器包含了一条自己没有的事务记录,那么就需要让该 Learner服务器进行事务回滚,回滚到 Leader服务器上存在的,同时也是最接近于 peerLastZxid的 ZXID;

仅回滚同步(TRUNC同步):peerLastZxid 大于 maxCommittedLog;

全量同步(SNAP同步):① peerLastZxid 小于 minCommittedLog;Leader服务器上没有 Proposal缓存队列且 peerLastZxid不等于 lastProcessZxid;

十二、Zookeeper是如何保证事务的顺序一致性的?


Zookeeper 采用了全局递增的事务ID 来标识,所有的 proposal(提议)都在被提出的时候加上了 zxid,zxid实际上是一个64位的数字,高32位是 epoch用来标识 Leader周期,如果有新的 Leader产生,epoch会自增,低32位用来递增计数。当新产生 proposal的时候,会依据数据库的两阶段过程,首先会向其它的 server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行。

十三、Zookeeper集群管理(文件系统、通知机制)


集群管理包含两点:是否有机器退出和加入、选举 Master。
对于第一点,所有机器约定在父目录下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 Zookeeper的连接断开,其所创建的临时目录节点被删除,所有其它机器都收到通知:某个兄弟目录被删除。新机器加入也类似,所有机器收到通知:新兄弟目录加入节点。对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为 Master就好。

十四、Zookeeper分布式锁(文件系统、通知机制)


有了 Zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一是保持独占,另一个是控制时序。
对于第一类,我们将 Zookeeper上的一个 znode看作是一把锁,通过 createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。
对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选 Master一样,编号最小的获得锁,用完删除,依次方便。

十五、获取分布式锁的流程


在获取分布式锁的时候在 locker节点下创建临时顺序节点,释放锁的时候删除该临时节点。客户端调用 createNode方法在 locker下创建临时顺序节点,然后调用 getChildren(“locker”)来获取 locker下面的所有子节点,注意此时不用设置任何 Watcher。客户端获取到所有的子节点 path之后,如果发现自己创建的节点在所有创建的子节点序号最小,那么就认为该客户端获取到了锁。如果发现自己创建的节点并非 locker所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,然后对其调用 exist()方法同时对其注册事件监听器。之后,这个被关注的节点删除,则客户端的 Watcher会收到相应通知,此时再次判断自己创建的节点是否是 locker子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。当前这个过程中还需要许多的逻辑判断。

代码的实现主要是基于互斥锁,获取分布式锁的重点逻辑在于 BaseDistributedLock,实现了基于 Zookeeper实现分布式锁的细节。

十六、Zookeeper 使用那种设计模式?


观察者模式【链接

十七、集群支持动态添加机器吗?


其实就是水平扩容了,Zookeeper在这方面不太好。两种方式:
【1】全部重启:关闭所有 Zookeeper服务,修改配置之后启动。不影响之前客户端的会话。
【2】逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常用的方式。

3.5版本开始支持动态扩容。

十八、Zookeeper数据复制


Zookeeper 作为一个集群提供一致的数据服务,自然,它要在所有机器间做数据复制。数据复制的好处:
【1】容错一个节点出错,不致于让整个系统停止工作,别的节点可以接管它的工作;
【2】提高系统的扩展能力 :把负载分布到多个节点上,或者增加节点来提高系统的负载能力;
【3】提高性能:让客户端本地访问就近的节点,提高用户访问速度

从客户端读写访问的透明度来看,数据复制集群系统分下面两种:
【1】写主(WriteMaster) :对数据的修改提交给指定的节点。读无此限制,可以读取任何一个节点。这种情况下客户端需要对读与写进行区别,俗称读写分离
【2】写任意(Write Any):对数据的修改可提交给任意的节点,跟读一样。这种情况下,客户端对集群节点的角色与变化透明。

对 Zookeeper来说,它采用的方式是写任意。通过增加机器,它的读吞吐能力和响应能力扩展性非常好,而写,随着机器的增多吞吐能力肯定下降(这也是它建立 observer的原因),而响应能力则取决于具体实现方式,是延迟复制保持最终一致性,还是立即复制快速响应。

Java 面试——Zookeeper的更多相关文章

  1. Java面试大纲-java面试该做哪些准备,java开发达到这样的水平可以涨工资

    Java培训结束,面临的就是毕业找工作.在找工作时,就要针对性地做充分的面试准备.准备不充分的面试,完全是浪费时间,更是对自己的不负责. 上海尚学堂Java培训整理出Java面试大纲,其中大部分都是面 ...

  2. Java面试宝典(2018版)

    置顶 2018年11月10日 23:49:18 我要取一个响亮的昵称 阅读数:8893    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/chen ...

  3. Java面试必刷常见真题200+ ,让你“过五关,斩六将”,轻松入大厂

    这份面试清单是我从 2015 年做 TeamLeader 之后开始收集的,一方面是给公司招聘用,另一方面是想用它来挖掘我在 Java 技术栈中的技术盲点,然后修复和完善它,以此来提高自己的技术水平.虽 ...

  4. java面试宝典2019(好东西先留着)

    java面试宝典2019 1.meta标签的作用是什么 2.ReenTrantLock可重入锁(和synchronized的区别)总结 3.Spring中的自动装配有哪些限制? 4.什么是可变参数? ...

  5. 《Java面试全解析》505道面试题详解

    <Java面试全解析>是我在 GitChat 发布的一门电子书,全书总共有 15 万字和 505 道 Java 面试题解析,目前来说应该是最实用和最全的 Java 面试题解析了. 我本人是 ...

  6. 【面试突击】- Java面试总则

    Java基础 1.Map.Set.List集合差别及联系详解 2.HashSet类是如何实现添加元素保证不重复的 3.HashMap 是线程安全的吗,为什么不是线程安全的(最好画图说明多线程环境下不安 ...

  7. 8年经验面试官详解 Java 面试秘诀

      作者 | 胡书敏 责编 | 刘静 出品 | CSDN(ID:CSDNnews) 本人目前在一家知名外企担任架构师,而且最近八年来,在多家外企和互联网公司担任Java技术面试官,前后累计面试了有两三 ...

  8. 《Java面试全解析》1000道面试题大全详解(转)

    <Java面试全解析>1000道 面试题大全详解 本人是 2009 年参加编程工作的,一路上在技术公司摸爬滚打,前几年一直在上海,待过的公司有 360 和游久游戏,因为自己家庭的原因,放弃 ...

  9. 【惊喜】Github爆火的java面试神技+java核心面试技术已开发下载,大厂内都传疯了!

    前言 今年,由于疫情的影响,很多互联网企业都在缩减招聘成本.作为程序员,原本这两年就面临竞争激烈.年龄危机的问题,而现在的求职局面又完全是企业在挑人的状态. 所以最好能在空闲的时候看看大厂相匹配的技术 ...

  10. 三面阿里,被Java面试官虐哭!现场还原真实的“被虐”场景

    前言 人人都有大厂梦,我也不例外,从大三开始,就一直想进入阿里工作,大毕竟是大厂,想想也没那么容易,不过好在自己学历还过得去,项目经验也有得讲,所以今年也斗胆尝试了一下,直接就投了阿里云计算.简历是过 ...

随机推荐

  1. Samsung Wlan AP 默认口令

    网络资产搜索: FoFa 进入页面 输入该产品账户密码   在github上面寻找 End!!!

  2. POJ--2689-C++

    题意很简单就是让你求给定区间的素数,然后用一个循环求出相距最远的相邻素数数和最近的素数以及相距最近的相邻素数 难点在与数据很大,所以不可能直接对区间的每一个数进行素数判断.但是,每个合数n都至少有一个 ...

  3. 【已解决】Jenkins构建成功但发送邮件失败,报错“Not sending mail to unregistered user xxx@xxx.com because your SCM claimed this was associated with a user ID ‘xxx which your security realm does not recognize; ”

    问题描述:构建成后,但发送邮件失败,具体报错截图如下: 原因:用户在jenkins中名称与发送邮件汇总设置不一样且没有勾选"Allow sending to unregistered use ...

  4. 使用类的习题(c++ prime plus)

    第一题 vect.h: #ifndef VECTOR_H_ #define VECTOR_H_ #include <iostream> namespace VECTOR { class V ...

  5. C语言初级阶段7——指针1

    C语言初级阶段7--指针1 地址与指针 1.地址:数据在内存中的存储位置编号,是一个常量. 2.指针:指针的本质就是地址. 指针变量的定义和声明 1.指针变量:存储的数据是地址. 2.定义方法:类型* ...

  6. HTML第四章作业

    学生实践4.1.3 1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8" ...

  7. Pintia 7-3 列车调度

    7-3 列车调度 (25 分) 火车站的列车调度铁轨的结构如下图所示. 两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有N条平行的轨道.每趟列车从入口可以选择任意一条轨 ...

  8. 问题积累 - IAR - ErrorTa97:Cannot callintrinsic functionnounwwind _DSBfrom Thumb mode in this architecture

    IAR编译工程时报错: ErrorTa97:Cannot callintrinsic functionnounwwind _DSBfrom Thumb mode in this architectur ...

  9. PHP判断0和空的方法

    可以兼容,传参数,或者不参数与0的判断   if ( isset($data['other_id']) && (!empty($data['other_id']) || is_nume ...

  10. Manage your references to .Net assemblies Dynamics 365 for Operations VS projects

    (Dynamics 365 for Operations was previously known as the New Dynamics AX) Dynamics 365 for Operation ...