• hadoop生态圈面试精华之zookeeper(一)


    hadoop生态圈面试精华之zookeeper(一)

    Zookeeper面试题

    介绍下Zookeeper是什么?
    可回答:谈谈你对Zookeeper的理解
    问过的一些公司:京东x2,字节,美团x2,蘑菇街,映客直播参考答案:
    Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。
    Zookeeper从设计模式角度来理解,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和 管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生了变化,Zookeeper就负责 通知已经在Zookeeper上注册的那些观察者做出相应的反应。
    Zookeeper提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、 软负载均衡等。它致力于为那些高吞吐的大型分布式系统提供一个高性能、高可用、且具有严格顺序访 问控制能力的分布式协调服务。
    特性

    1. 顺序一致性:从一个客户端发起的事务请求,最终都会严格按照其发起顺序被应用到Zookeeper中; 对于来自客户端的每个更新请求,Zookeeper都会分配一个全局唯一的递增ID(zxid),这个ID反映了所有 事务请求的先后顺序。
    2. 原子性:所有事务请求的处理结果在整个集群中所有机器上都是一致的
    3. 最终一致性:所有客户端看到的服务端数据模型都是一致的;
    4. 可靠性:一旦服务端成功应用了一个事务,则其引起的改变会一直保留,直到被另外一个事务所更 改,如果消息被到一台服务器接受,那么它将被所有的服务器接受。
    5. 实时性:一旦一个事务被成功应用后,Zookeeper可以保证客户端立即可以读取到这个事务变更后的 最新状态的数据。
    6. 等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效 的等待。
      由于Zookeeper的所有更新和删除都是基于事务的,所以其在读多写少的应用场景中有着很高的性能表 现。
      ZooKeeper将数据存全量储在内存中以保持高性能,并通过服务集群来实现高可用。 可结合下一题一起回答

    Zookeeper有什么作用?优缺点?有什么应用场景?
    可回答:你觉得Zookeeper比较重要的功能
    问过的一些公司:阿里x3,京东x3,阿里,字节,字节(2021.10),美团x3,蘑菇街x2,深信服,头条,
    vivo,ebay
    参考答案:
    作用
    Zookeeper作用包括存储数据(文件系统)和监听(监听通知机制)
    优点
    7. 分布式协调过程简单
    8. 同步:zk高度同步,这意味着服务器进程之间既存在互斥又存在合作,同步有助于Apache HBase进行配置管理。
    9. 有序消息:zk跟踪一个数字,表示每个更新的顺序,保证消息有序
    10. 序列化:根据具体规则,zk对数据进行编码。 另外,它还可确保我们的应用程序始终如一地运行。但是,在MapReduce中,我们使用此方法(序列化)来协调队列以执行正在运行的线程
    11. 速度:在读请求多的情况下,能以很快的速度运行
    12. 可扩展性:此外,可以通过部署更多机器来加强zk的性能
    13. 有序性有何优势?:众所周知,zk中的消息是有序的。所以,为了实现更高级别的抽象,需要有序 性。 这就是有序性对我们有利的方式
    14. 快:在读多的情况下,zk会非常快
    15. 可靠性:zk非常可靠,因为一旦zk更新了,更新后的数据会一直保持,直到被覆盖更新
    16. 原子性:zk只有两种情况,要么全部成功,要么全部失败,没有中间状态的情况
    17. 实时性:zk保证在一定时间段内,客户端最终一定能从服务器上读到最新的数据状态
    缺点
    18. 增加新的zk服务器时可能导致数据丢失
    在现有服务器中,当新zk服务器数量超过zk服务中已存在的数量时数据会丢失。 同时,向zk服务发出
    Start命令,新服务器可能形成仲裁
    19. 不能迁移
    在没有用户干预的情况下,zk服务器无法从版本3.4迁移到3.3,然后再迁移到3.4。
    20. 节点数(其实集群中只要存活数过半即对外可用,但是会有脑裂情况发生)
    要求3或5个这样奇数个zk节点(要求奇数是为了保证选举的正常进行因为leader选举要求 可用节点数量

    总节点数/2,防止脑裂造成集群不可用。同时在容错能力相同的情况下,奇数个节点更节省资源)

    1. 机架感知复制
      目前,它不支持机架放置和感知
    2. 缩容
      不支持减少pod的数量,以防止意外数据丢失
    3. 磁盘变更
      不支持在初始部署后更改volume要求,以防止重新分配意外丢失数据
    4. 虚拟网络
      当服务部署在虚拟网络上时,如果没有完全重新安装,服务可能无法切换到主机网络。 另外,对于尝试从主机切换到虚拟网络,它们是相同的情况
    5. Kerberos
      在虚拟网络上,它目前不支持启用Kerberos 9)支持有限
      对跨群集方案的支持非常有限。 但是,没有CP系统会一直支持跨集群。 虽然我们可以说consul似乎在这方面做得更好
      应用场景
    6. 数据的发布/订阅
      数据的发布/订阅系统,通常也用作配置中心。在分布式系统中,你可能有成千上万个服务节点,如果 想要对所有服务的某项配置进行更改,由于数据节点过多,你不可逐台进行修改,而应该在设计时采用 统一的配置中心。之后发布者只需要将新的配置发送到配置中心,所有服务节点即可自动下载并进行更 新,从而实现配置的集中管理和动态更新。 Zookeeper通过Watcher机制可以实现数据的发布和订阅。分布式系统的所有的服务节点可以对某个ZNode注册监听,之后只需要将新的配置写入该ZNode,所有服 务节点都会收到该事件。
    7. 命名服务
      在分布式系统中,通常需要一个全局唯一的名字,如生成全局唯一的订单号等,Zookeeper可以通过顺 序节点的特性来生成全局唯一ID,从而可以对分布式系统提供命名服务。
    8. Master选举
      分布式系统一个重要的模式就是主从模式(Master/Salves),Zookeeper可以用于该模式下的Matser选举。 可以让所有服务节点去竞争性地创建同一个ZNode,由于Zookeeper不能有路径相同的ZNode,必然只有 一个服务节点能够创建成功,这样该服务节点就可以成为Master节点。
    9. 分布式锁
      可以通过Zookeeper的临时节点和Watcher机制来实现分布式锁,这里以排它锁为例进行说明:分布式系 统的所有服务节点可以竞争性地去创建同一个临时ZNode,由于Zookeeper不能有路径相同的ZNode,必 然只有一个服务节点能够创建成功,此时可以认为该节点获得了锁。其他没有获得锁的服务节点通过在 该ZNode上注册监听,从而当锁释放时再去竞争获得锁。锁的释放情况有以下两种: 当正常执行完业务逻辑后,客户端主动将临时ZNode删除,此时锁被释放; 当获得锁的客户端发生宕机时,临时ZNode会被自动删除,此时认为锁已经释放。 当锁被释放后,其他服务节点则再次去竞争性地进行创建,但每次都只有一个服务节点能够获取到锁,这就是排他锁。
    10. 集群管理
      Zookeeper还能解决大多数分布式系统中的问题: 如可以通过创建临时节点来建立心跳检测机制。如果分布式系统的某个服务节点宕机了,则其持有的会话会超时,此时该临时节点会被删除,相应的监听事 件就会被触发。 分布式系统的每个服务节点还可以将自己的节点状态写入临时节点,从而完成状态报告或节点工作进度汇报。 通过数据的订阅和发布功能,Zookeeper还能对分布式系统进行模块的解耦和任务的调度。 通过监听机制,还能对分布式系统的服务节点进行动态上下线,从而实现服务的动态扩容。
    11. 事务操作
      在ZooKeeper中,能改变ZooKeeper服务器状态的操作称为事务操作。一般包括数据节点创建与删除、数 据内容更新和客户端会话创建与失效等操作。 对应每一个事务请求,ZooKeeper都会为其分配一个全局唯一的事务ID,用 ZXID 表示,通常是一个64位的数字。每一个 ZXID对应一次更新操作, 从这些 ZXID 中可以间接地识别出 ZooKeeper 处理这些事务操作请求的全局顺序。

    Zookeeper的选举策略,leader和follower的区别?
    可回答:1)Zookeeper的选举过程(选举机制);2)leader的选举是如何实现的;3)说说Zookeeper的 启动过程,比如现在有五台机器,ABCDE依次启动起来,那么哪台是leader?4)Zookeeper的选主策略 了解过吗
    问过的一些公司:阿里,字节,字节(2021.10),去哪儿,网易,贝壳,京东,蘑菇屋,端点数据
    (2021.07),华为精英计划(2021.07),大华(2021.08)
    参考答案:
    半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
    Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
    以一个简单的例子来说明整个选举的过程
    假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史 数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么。
    在这里插入图片描述
    32. 服务器1启动,发起一次选举。服务器1投自己一票。此时服务器1票数一票,不够半数以上(3
    票),选举无法完成,服务器1状态保持为LOOKING;
    33. 服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息:此时服务器1发现服 务器2的ID比自己目前投票推举的(服务器1)大,更改选票为推举服务器2。此时服务器1票数0票,服 务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING
    34. 服务器3启动,发起一次选举。此时服务器1和2都会更改选票为服务器3。此次投票结果:服务器1为
    0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数,服务器3当选Leader。服务器
    1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
    35. 服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交 换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,并 更改状态为FOLLOWING;
    36. 服务器5启动,同4一样当小弟。
    领导者Leader
    Leader在集群中只有一个节点,可以说是老大,是zookeeper集群的中心,负责协调集群中的其他节点。 从性能的角度考虑,leader可以选择不接受客户端的连接。
    主要作用
    37. 发起与提交写请求。
    所有的跟随者Follower与观察者Observer节点的写请求都会转交给领导者Leader执行。Leader接受到一 个写请求后,首先会发送给所有的Follower,统计Follower写入成功的数量。当有超过半数的Follower写 入成功后,Leader就会认为这个写请求提交成功,通知所有的Follower commit这个写操作,保证事后哪怕是集群崩溃恢复或者重启,这个写操作也不会丢失。
    38. 与Learner(Follower与Observer)保持心跳 3)崩溃恢复时负责恢复数据以及同步数据到Learner 跟随者Follower
    Follow在集群中有多个
    主要作用
    39. 与老大Leader保持心跳连接
    40. 当Leader挂了的时候,经过投票后成为新的leader。leader的重新选举是由老二Follower们内部投票决 定的。
    41. 向leader发送消息与请求 4)处理leader发来的消息与请求

    介绍下Zookeeper选举算法
    问过的一些公司:海康(2021.08) 参考答案:
    在ZooKeeper中,提供了三种Leader选举的算法,分别是
    42. LeaderElection
    43. UDP版本的FastLeaderElection 3)TCP版本的FastLeaderElection
    可以通过在配置文件zoo.cfg中使用electionAlg属性来指定,分别使用数字0~3来表示。0代表LeaderElection,这是一种纯UDP实现的Leader选举算法:1代表UDP版本的FastLeaderElection,并且是非 授权模式;2也代表UDP版本的FastLeaderElection,但使用授权模式;3代表TCP版本的FastLeaderElection。不过从3.4.0版本开始,ZooKeeper废弃了0、1、2这三种Leader选举算法,只保留了 TCP版本的FastLeaderElection选举算法。
    1、Leader选举

    Leader选举是保证分布式数据一致性的关键所在。当Zookeeper集群中的一台服务器出现以下两种情况 之一时,需要进入Leader选举。
    44. 服务器初始化启动。(集群的每个节点都没有数据 → 以SID的大小为准)
    45. 服务器运行期间无法和Leader保持连接。(集群的每个节点都有数据 ,或者Leader 宕机→ 以ZXID 和
    SID 的最大值为准)

    47.1 服务器启动时期的Leader选举
    若进行Leader选举,则至少需要2台机器,两台的高可用性会差一些,如果Leader 宕机,就剩下一台, 自己没办法选举。一般集群也是3台机器,这里选取3台机器组成的服务器集群为例。
    在集群初始化阶段,当有一台服务器Server1启动时,其单独无法进行和完成Leader选举,当第二台服务 器Server2启动时,此时两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程如下
    46. 每个Server发出一个投票。由于是初始情况,Server1和Server2都会将自己作为Leader服务器来进行 投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时Server1的投票为(1, 0),Server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
    47. 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是 否是本轮投票、是否来自LOOKING状态的服务器。
    48. 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
    优先检查ZXID。ZXID比较大的服务器优先作为Leader。(这个很重要:是数据最新原则,保证数 据的完整性)
    如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器。(集群的节点标识)
    对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0。再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于Server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
    49. 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信 息,对于Server1、Server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了Leader。
    50. 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变 更为FOLLOWING,如果是Leader,就变更为LEADING。

    47.2 服务器运行时期的Leader选举
    在Zookeeper运行期间,Leader与非Leader服务器各司其职,即便当有非Leader服务器宕机或新加入,此 时也不会影响Leader,但是一旦Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮Leader 选举,其过程和启动时期的Leader选举过程基本一致。
    假设正在运行的有Server1、Server2、Server3三台服务器,当前Leader是Server2,若某一时刻Leader挂 了,此时便开始Leader选举。
    选举过程如下:
    52. 变更状态。Leader挂后,余下的非Observer服务器都会讲自己的服务器状态变更为LOOKING,然后开 始进入Leader选举过程。
    53. 每个Server会发出一个投票。在运行期间,每个服务器上的ZXID可能不同,此时假定Server1的ZXID 为123,Server3的ZXID为122;在第一轮投票中,Server1和Server3都会投自己,产生投票(1, 123),(3, 122),然后各自将投票发送给集群中所有机器。
    54. 接收来自各个服务器的投票。与启动时过程相同。
    55. 处理投票。与启动时过程相同,此时,Server1将会成为Leader。
    56. 统计投票。与启动时过程相同。
    57. 改变服务器的状态。与启动时过程相同。
    2、Leader选举算法分析
    最开始也提过,在3.4.0后的Zookeeper的版本只保留了TCP版本的FastLeaderElection选举算法。当一台机 器进入Leader选举时,当前集群可能会处于以下两种状态
    集群中已经存在Leader。集群中不存在Leader。
    对于集群中已经存在Leader而言,此种情况一般都是某台机器启动得较晚,在其启动之前,集群已经在 正常工作,对这种情况,该机器试图去选举Leader时,会被告知当前服务器的Leader信息,对于该机器 而言,仅仅需要和Leader机器建立起连接,并进行状态同步即可。
    而在集群中不存在Leader情况下则会相对复杂,其步骤如下
    58. 第一次投票。无论哪种导致进行Leader选举,集群的所有机器都处于试图选举出一个Leader的状态,即LOOKING状态,LOOKING机器会向所有其他机器发送消息,该消息称为投票。投票中包含了SID(服务器的唯一标识)和ZXID(事务ID),(SID, ZXID)形式来标识一次投票信息。假定Zookeeper由5
    台机器组成,SID分别为1、2、3、4、5,ZXID分别为9、9、9、8、8,并且此时SID为2的机器是Leader机 器,某一时刻,1、2所在机器出现故障,因此集群开始进行Leader选举。在第一次投票时,每台机器都 会将自己作为投票对象,于是SID为3、4、5的机器投票情况分别为(3, 9),(4, 8), (5, 8)。
    59. 变更投票。每台机器发出投票后,也会收到其他机器的投票,每台机器会根据一定规则来处理收到 的其他机器的投票,并以此来决定是否需要变更自己的投票,这个规则也是整个Leader选举算法的核心 所在,其中术语描述如下
    vote_sid:接收到的投票中所推举Leader服务器的SID。vote_zxid:接收到的投票中所推举Leader服务器的ZXID。self_sid: 当 前 服 务 器 自 己 的 SID 。 self_zxid:当前服务器自己的ZXID。
    每次对收到的投票的处理,都是对(vote_sid, vote_zxid)和(self_sid, self_zxid)对比的过程。规则一:如果vote_zxid大于self_zxid,就认可当前收到的投票,并再次将该投票发送出去。 规则二:如果vote_zxid小于self_zxid,那么坚持自己的投票,不做任何变更。
    规则三:如果vote_zxid等于self_zxid,那么就对比两者的SID,如果vote_sid大于self_sid,那么就认可当 前收到的投票,并再次将该投票发送出去。
    规则四:如果vote_zxid等于self_zxid,并且vote_sid小于self_sid,那么坚持自己的投票,不做任何变 更。
    结合上面规则,给出下面的集群变更过程。

    在这里插入图片描述
    60. 确定Leader。经过第二轮投票后,集群中的每台机器都会再次接收到其他机器的投票,然后开始统 计投票,如果一台机器收到了超过半数的相同投票,那么这个投票对应的SID机器即为Leader。此时Server3将成为Leader。
    由上面规则可知,通常那台服务器上的数据越新(ZXID会越大),其成为Leader的可能性越大,也就越 能够保证数据的恢复。如果ZXID相同,则SID越大机会越大。

    3、Leader选举实现细节
    a. 服务器状态
    服务器具有四种状态,分别是LOOKING、FOLLOWING、LEADING、OBSERVING。
    LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,因此需要进入
    Leader选举状态。
    FOLLOWING:跟随者状态。表明当前服务器角色是Follower。LEADING:领导者状态。表明当前服务器角色是Leader。OBSERVING:观察者状态。表明当前服务器角色是Observer。
    47.3 投票数据结构
    每个投票中包含了两个最基本的信息,所推举服务器的SID和ZXID,投票(Vote)在Zookeeper中包含字 段如下
    id:被推举的Leader的SID。
    zxid:被推举的Leader事务ID。
    electionEpoch:逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一个自增序 列,每次进入新一轮的投票后,都会对该值进行加1操作。
    peerEpoch:被推举的Leader的epoch。
    state:当前服务器的状态。

    Zookeeper的节点类型有哪些?分别作用是什么?
    可回答:说下Zookeeper的临时节点和永久节点问过的一些公司:字节,eBay,快手,蘑菇街 参考答案:
    Znode有两种类型:
    短暂(ephemeral):客户端和服务器端断开连接后,创建的节点自己删除

    持久(persistent):客户端和服务器端断开连接后,创建的节点不删除Znode 有 四 种 形 式 的 目 录 节 点 ( 默 认 是 persistent ) 1)持久化目录节点(PERSISTENT)
    客户端与zookeeper断开连接后,该节点依旧存在

    1. 持久化顺序编号目录节点(PERSISTENT_SEQUENTIAL)
      客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
    2. 临时目录节点(EPHEMERAL)
      客户端与zookeeper断开连接后,该节点被删除
    3. 临时顺序编号目录节点(EPHEMERAL_SEQUENTIAL)
      客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
      创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点 维护
      在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过顺序号推断事件 的顺序

    Zookeeper的节点数怎么设置比较好?
    可回答:Zookeeper集群一般设置多少个节点,允许几个节点挂问过的一些公司:bigo,华为精英计划(2021.07)
    参考答案:
    2n+1,半数机制。一般是3个或者3个以上节点。

    Zookeeper架构
    在这里插入图片描述
    问过的一些公司:字节参考答案:
    Zookeeper本身可以是单机模式,也可以是集群模式,为了Zookeeper本身不出现单点故障,通常情况下 使用集群模式,而且是master/slave模式的集群
    Zookeeper集群角色

    Leader(领导者)
    Leader不直接接收client的请求,但接受由其他Follower和Observer转发过来的client请求。此外,Leader
    还负责投票的发起和决议,即时更新状态和数据。

    Follower(跟随者)
    Follower角色接手客户端请求并返回结果,参与Leader发起的投票和选举,但不具有写操作的权限。

    Observer(观察者)
    Observer角色接受客户端连接,将写操作转给Leader,但Observer不参与投票(即不参加一致性协议的 达成),只同步Leader节点的状态,Observer角色是为集群系统扩展而生的。

    Clinet(客户端)
    连接Zookeeper集群的使用者,请求的发起者,独立于Zookeeper集群的角色。

    Zookeeper的功能有哪些
    可回答:Zookeeper的作用
    问过的一些公司:字节,阿里,美团x2,蘑菇街
    Zookeeper作用包括存储数据(文件系统)和监听(监听通知机制)

    Zookeeper的数据结构(树)?基于它实现的分布式锁?基于它实现的Master选举?基于它的集群管理?Zookeeper的注册(watch)机制使用场景?
    问过的一些公司:阿里,ebay 参考答案:
    1、数据结构
    Zookeeper数据模型的结构与Unix文件系统很类似,整体上可以看作一棵树,每个节点称作一个ZNode。 每一个ZNode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识。
    在这里插入图片描述
    ZNode有两种类型
    短暂(ephemeral):客户端和服务器端断开连接后,创建的节点自己删除 持久(persistent):客户端和服务器端断开连接后,创建的节点不删除
    ZNode有四种形式的目录节点(默认是PERSISTENT)

    类型 描述
    PERSISTENT 持久化节点

    PERSISTENT_SEQUENTIAL 顺序自动编号持久化节点,这种节点会根据当前已存在的节点数自动+1
    EPHEMERAL 临时节点,客户端session超时这类节点会被自动删除
    EPHEMERAL_SEQUENTIAL 临时自动编号节点
    2、分布式锁
    a. 为什么需要分布式锁
    锁是多线程代码中的概念,只有当多任务访问同一个互斥的共享资源时才需要。如下图:
    在这里插入图片描述
    在我们进行单机应用开发,涉及并发同步的时候,我们往往采用synchronized或者Lock的方式来解决多 线程间的代码同步问题,这时多线程的运行都是在同一个JVM之下。但当我们的应用是分布式集群工作 的情况下,属于多JVM下的工作环境,JVM之间已经无法通过多线程的锁解决同步问题。那么就需要一 种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题——这就是分布式锁。
    如下例:携程、美团、飞猪、去哪儿四个购票网站实际上都没有最终售票权,只有12306铁道总局有火 车票,那么四个购票网站都需要购买火车票,那么四个网站必须排队进行同步,否则不同步会造成多售
    (类似同一进程中多线程间不同步也会造成多售)。这时他们就需要有一个公共的锁管理方案,来保证
    APP间的购票是同步的。要想购票:

    1. 首先服务获取分布式
    2. 服务获取分布式锁后,才能去调用12306进行购票。
    3. 购票成功后,释放分布式锁,这样其余APP才能获取锁并进行12306购票。
      在这里插入图片描述
      47.4 为什么要使用Zookeeper 基于mysql实现分布式锁
      基于分布式锁的实现,首先肯定是想单独分离出一台mysql数据库,所有服务要想操作文件(共享资 源),那么必须先在mysql数据库中插入一个标志,插入标志的服务就持有了锁,并对文件进行操作,操作完成后,主动删除标志进行锁释放,其与服务会一直查询数据库,看是否标志有被占用,直到没有 标志占用时自己才能写入标志获取锁。但是这样有这么一个问题,如果服务(jvm1)宕机或者卡顿了,会一直持有锁未释放,这样就造成了死 锁,因此就需要有一个监视锁进程时刻监视锁的状态,如果超过一定时间未释放就要进行主动清理锁标 记,然后供其与服务继续获取锁。
      如果监视锁字段进程和jvm1同时挂掉,依旧不能解决死锁问题,于是又增加一个监视锁字段进程,这样 一个进程挂掉,还有另一个监视锁字段进程可以对锁进行管理。这样又诞生一个新的问题,两个监视进 程必须进行同步,否则对于过期的情况管理存在不一致问题。
      因此存在以下问题,并且方案变得很复杂:
    4. 监视锁字段进程对于锁的监视时间周期过短,仍旧会造成多售(jvm1还没处理完其持有的锁就被主 动销毁,造成多个服务同时持有锁进行操作)。
    5. 监视锁字段进程对于锁的监视时间周期过长,会造成整个服务卡顿过长,吞吐低下。
    6. 监视锁字段进程间的同步问题。
    7. 当一个jvm持有锁的时候,其余服务会一直访问数据库查看锁,会造成其余jvm的资源浪费。

    在这里插入图片描述
    基于Redis实现分布式锁
    相比较于基于数据库实现分布式锁的方案来说,基于缓存来实现在性能方面会表现的更好一点,Redis就 是其中一种。由于Redis可以设置字段的有效期,因此可以实现自动释放超期的锁,不需要多个监视锁字 段进程进行锁守护,可以依旧存在上述mysql实现中除了3以外1、2、4中的问题。
    在这里插入图片描述

    基于Zookeeper实现分布式锁
    相比于前面两种实现方式,基于zookeeper实现分布式锁的方案优于上述两种方案。由于zookeeper有以 下特点:

    1. 维护了一个有层次的数据节点,类似文件系统。
    2. 有以下数据节点:临时节点、持久节点、临时有序节点(分布式锁实现基于的数据节点)、持久有 序节点。
    3. zookeeper可以和client客户端通过心跳的机制保持长连接,如果客户端链接zookeeper创建了一个临 时节点,那么这个客户端与zookeeper断开连接后会自动删除。
    4. zookeeper的节点上可以注册上用户事件(自定义),如果节点数据删除等事件都可以触发自定义事 件。
    5. zookeeper保持了统一视图,各服务对于状态信息获取满足一致性。Zookeeper的每一个节点,都是一个天然的顺序发号器。
      在每一个节点下面创建子节点时,只要选择的创建类型是有序(EPHEMERAL_SEQUENTIAL 临时有序或者PERSISTENT_SEQUENTIAL 永久有序)类型,那么,新的子节点后面,会加上一个次序编号。这个次序编号,是上一个生成的次序编号加一。比如,创建一个用于发号的节点“/test/lock”,然后以他为父亲节点,可以在这个父节点下面创建相同前 缀的子节点,假定相同的前缀为“/test/lock/seq-”,在创建子节点时,同时指明是有序类型。如果是第一 个创建的子节点,那么生成的子节点为/test/lock/seq-0000000000,下一个节点则为/test/lock/seq- 0000000001,依次类推,等等。
      在这里插入图片描述
      3、如何使用Zookeeper实现分布式锁
      大致思想为:每个客户端对某个方法加锁时,在 Zookeeper 上与该方法对应的指定节点的目录下,生成一个唯一的临时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。当释放锁的时候,只需将这个临时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产 生的死锁问题。
      47.5 排它锁
      排他锁,又称写锁或独占锁。如果事务T1对数据对象O1加上了排他锁,那么在整个加锁期间,只允许事 务T1对O1进行读取或更新操作,其他任务事务都不能对这个数据对象进行任何操作,直到T1释放了排他 锁。
      排他锁核心是保证当前有且仅有一个事务获得锁,并且锁释放之后,所有正在等待获取锁的事务都能够 被通知到。
      Zookeeper 的强一致性特性,能够很好地保证在分布式高并发情况下节点的创建一定能够保证全局唯一性,即Zookeeper将会保证客户端无法重复创建一个已经存在的数据节点。可以利用Zookeeper这个特 性,实现排他锁。
    6. 定义锁:通过Zookeeper上的数据节点来表示一个锁
    7. 获取锁:客户端通过调用 create 方法创建表示锁的临时节点,可以认为创建成功的客户端获得了锁,同时可以让没有获得锁的节点在该节点上注册Watcher监听,以便实时监听到lock节点的变更情况
    8. 释放锁:以下两种情况都可以让锁释放
      当前获得锁的客户端发生宕机或异常,那么Zookeeper上这个临时节点就会被删除 正常执行完业务逻辑,客户端主动删除自己创建的临时节点

    基于Zookeeper实现排他锁流程:
    在这里插入图片描述
    47.6 共享锁
    共享锁,又称读锁。如果事务T1对数据对象O1加上了共享锁,那么当前事务只能对O1进行读取操作, 其他事务也只能对这个数据对象加共享锁,直到该数据对象上的所有共享锁都被释放。
    共享锁与排他锁的区别在于,加了排他锁之后,数据对象只对当前事务可见,而加了共享锁之后,数据 对象对所有事务都可见。

    1. 定义锁:通过Zookeeper上的数据节点来表示一个锁,是一个类似于/lockpath/[hostname]-请求类型-
      序号的临时顺序节点

    2. 获取锁:客户端通过调用 create 方法创建表示锁的临时顺序节点,如果是读请求,则创建
      /lockpath/[hostname]-R-序号节点,如果是写请求则创建 /lockpath/[hostname]-W-序号节点

    3. 判断读写顺序:大概分为4个步骤
      ① 创建完节点后,获取 /lockpath 节点下的所有子节点,并对该节点注册子节点变更的Watcher监听
      ② 确定自己的节点序号在所有子节点中的顺序
      ③ 对于读请求:
      如果没有比自己序号更小的子节点,或者比自己序号小的子节点都是读请求,那么表明自己已经成 功获取到了共享锁,同时开始执行读取逻辑
      如果有比自己序号小的子节点有写请求,那么等待
      对于写请求,如果自己不是序号最小的节点,那么等待
      ④ 接收到Watcher通知后,重复步骤①

    4. 释放锁:与排他锁逻辑一致
      在这里插入图片描述
      基于Zookeeper实现共享锁流程:
      在这里插入图片描述
      47.7 羊群效应
      在实现共享锁的 “判断读写顺序” 的第1个步骤是:创建完节点后,获取 /lockpath 节点下的所有子节点,并对该节点注册子节点变更的Watcher监听。这样的话,任何一次客户端移除共享锁之后, Zookeeper将会发送子节点变更的Watcher通知给所有机器,系统中将有大量的 “Watcher通知” 和 “子节
      点列表获取” 这个操作重复执行,然后所有节点再判断自己是否是序号最小的节点(写请求)或者判断比自己序号小的子节点是否都是读请求(读请求),从而继续等待下一次通知。

    然而,这些重复操作很多都是 “无用的”,实际上每个锁竞争者只需要关注序号比自己小的那个节点是否存在即可。
    当集群规模比较大时,这些 “无用的” 操作不仅会对Zookeeper造成巨大的性能影响和网络冲击,更为严重的是,如果同一时间有多个客户端释放了共享锁,Zookeeper服务器就会在短时间内向其余客户端发 送大量的事件通知,这就是所谓的 “羊群效应”。
    改进后的分布式锁实现:

    1. 客户端调用 create 方法创建一个类似于 /lockpath/[hostname]-请求类型-序号 的临时顺序节点。2)客户端调用 getChildren 方法获取所有已经创建的子节点列表(这里不注册任何Watcher)。3)如果无法获取任何共享锁,那么调用 exist 来对比自己小的那个节点注册Watcher
      读请求:向比自己序号小的最后一个写请求节点注册Watcher监听写请求:向比自己序号小的最后一个节点注册Watcher监听
      等待Watcher监听,继续进入步骤2)
      Zookeeper羊群效应改进前后Watcher监听图:
      在这里插入图片描述
      4、master选举使用场景及结构
      现在很多时候我们的服务需要7*24小时工作,假如一台机器挂了,我们希望能有其它机器顶替它继续工 作。此类问题现在多采用master-salve模式,也就是常说的主从模式,正常情况下主机提供服务,备机 负责监听主机状态,当主机异常时,可以自动切换到备机继续提供服务(这里有点儿类似于数据库主库 跟备库,备机正常情况下只监听,不工作),这个切换过程中选出下一个主机的过程就是master选举。
      对于以上提到的场景,传统的解决方式是采用一个备用节点,这个备用节点定期给当前主节点发送ping 包,主节点收到ping包后会向备用节点发送应答ack,当备用节点收到应答,就认为主节点还活着,让它 继续提供服务,否则就认为主节点挂掉了,自己将开始行使主节点职责。如图1所示:

    在这里插入图片描述
    但这种方式会存在一个隐患,就是网络故障问题。看一下图2:在这里插入图片描述
    也就是说,我们的主节点并没有挂掉,只是在备用节点ping主节点,请求应答的时候发生网络故障,这 样我们的备用节点同样收不到应答,就会认为主节点挂掉,然后备机会启动自己的master实例。这样就 会导致系统中有两个主节点,也就是双master。出现双master以后,我们的从节点会将它做的事情一部 分汇报给主节点,一部分汇报给备用节点,这样服务就乱套了。为了防止这种情况出现,我们可以考虑 采用zookeeper,虽然它不能阻止网络故障的出现,但它能保证同一时刻系统中只存在一个主节点。我 们来看zookeeper是怎么实现的:在此处,抢主程序是包含在服务程序中,需要程序员来手动写抢主逻 辑 的 , 比 如 当 当 开 源 框 架 elastic-job 中 , 就 有 关 于 选 主 的 部 分 , 参 见 :elastic-job- core/main/java/com/dangdang/ddframe/job/internal/election文件夹下的选主代码。
    Zookeeper自己在集群环境下的抢主算法有三种,可以通过配置文件来设定,默认采用FastLeaderElection,不作赘述;此处主要讨论集群环境中,应用程序利用master的特点,自己选主的过 程。程序自己选主,每个人都有自己的一套算法,有采用“最小编号”的,有采用类似“多数投票”的,各 有优劣,本文的算法仅作演示理解使用:
    结构图如下:
    在这里插入图片描述
    左侧树状结构为zookeeper集群,右侧为程序服务器。所有的服务器在启动的时候,都会订阅zookeeper 中master节点的删除事件,以便在主服务器挂掉的时候进行抢主操作;所有服务器同时会在servers节点 下注册一个临时节点(保存自己的基本信息),以便于应用程序读取当前可用的服务器列表。
    选主原理介绍:Zookeeper的节点有两种类型,持久节点跟临时节点。临时节点有个特性,就是如果注 册这个节点的机器失去连接(通常是宕机),那么这个节点会被zookeeper删除。选主过程就是利用这 个特性,在服务器启动的时候,去zookeeper特定的一个目录下注册一个临时节点(这个节点作为master,谁注册了这个节点谁就是master),注册的时候,如果发现该节点已经存在,则说明已经有别 的服务器注册了(也就是有别的服务器已经抢主成功),那么当前服务器只能放弃抢主,作为从机存 在。同时,抢主失败的当前服务器需要订阅该临时节点的删除事件,以便该节点删除时(也就是注册该 节点的服务器宕机了或者网络断了之类的)进行再次抢主操作。从机具体需要去哪里注册服务器列表的 临时节点,节点保存什么信息,根据具体的业务不同自行约定。选主的过程,其实就是简单的争抢在zookeeper注册临时节点的操作,谁注册了约定的临时节点,谁就是master。
    5、Watch机制
    a. 为什么添加Watch
    ZooKeeper是用来协调(同步)分布式进程的服务,提供了一个简单高性能的协调内核,用户可以在此 之上构建更多复杂的分布式协调功能。
    多个分布式进程通过ZooKeeper提供的API来操作共享的ZooKeeper内存数据对象ZNode来达成某种一致的 行为或结果,这种模式本质上是基于状态共享的并发模型,与Java的多线程并发模型一致,他们的线程 或进程都是”共享式内存通信“。Java没有直接提供某种响应式通知接口来监控某个对象状态的变化,只 能要么浪费CPU时间毫无响应式的轮询重试,或基于Java提供的某种主动通知(Notif)机制(内置队
    列)来响应状态变化,但这种机制是需要循环阻塞调用。而ZooKeeper实现这些分布式进程的状态
    (ZNode的Data、Children)共享时,基于性能的考虑采用了类似的异步非阻塞的主动通知模式即Watch 机制,使得分布式进程之间的“共享状态通信”更加实时高效,其实这也是ZooKeeper的主要任务决定的— 协调。Consul虽然也实现了Watch机制,但它是阻塞的长轮询。

    在这里插入图片描述

  • 相关阅读:
    20、动态路由_下滑线为前缀的目录
    电脑桌面便签工具选择哪一款?
    使用 Kotlin DSL 编写网络爬虫
    如何管理数据湖中的小文件
    教你使用Jupyter可视化查询语句的语法树
    英语音标中难发的音汇总
    RIP解决不连续子网问题
    uni-app调用Native.jsAPI实现对Android原生日历的增删查操作
    C#基于asp.net的社区团购网站
    快应用(安卓)keystore 获得应用签名详细流程
  • 原文地址:https://blog.csdn.net/m0_46914845/article/details/126610019