加入收藏 | 设为首页 | 会员中心 | 我要投稿 我爱制作网_池州站长网 (https://www.0566zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Unix > 正文

unix系统内幕 贝克街的流浪猫的博客

发布时间:2022-10-26 11:14:07 所属栏目:Unix 来源:
导读:  数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中心,顾名思义就是发布者将数据发布到ZooKeeper的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动
  数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中心,顾名思义就是发布者将数据发布到ZooKeeper的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
 
  如果将配置信息存放到ZooKeeper上进行集中管理,那么通常情况下,应用在启动的时候都会主动到 ZooKeeper服务端上进行次配置信息的获取,同时,在指定节点上注册一个 Watcher监听,这样一来,但凡配置信息发生变更,服务端都会实时通知到所有订阅的客户端,从而达到实时获取最新配置信息的目的。
 
  负载均衡
 
  基于ZooKeeper我们可以实现一个自动化的DNS服务,具体架构图如下所示:
 
  ddns
 
  首先来介绍整个动态DNS系统的架构体系中几个比较重要的组件及其职责。
 
  命名服务
 
  所谓命名服务,就是得到一个全局唯一的名字,类似于数据库上的主键。我们可以通过ZooKeeper的顺序节点功能,完成这个命名服务。
 
  分布式协调/通知
 
  分布式协调通知服务是分布式系统中不可缺少的一个环节,是将不同的分布式组件有机结合起来的关键所在。对于一个在多台机器上部署运行的应用而言,通常需要一个协调者(Coordinator)来控制整个系统的运行流程,例如分布式事务的处理、机器间的互相协调等。同时,引入这样一个协调者,便于将分布式协调的职责从应用中分离出来,从而可以大大减少系统之间的耦合性,而且能够显著提高系统的可扩展性。下列的即是一些协调例子:
 
  分布式锁
 
  在数据库中,锁的概念其实是非常重要的,常见的关系型数据库就会对排他锁和共享锁进行支持,而 Zookeeper 提供的 API 也可以让我们非常简单的实现分布式锁。
 
  如果多个服务同时要对某个资源进行修改,就可以使用上述的代码来实现分布式锁,假设集群中存在一个资源 /resource,几个服务需要通过分布式锁保证资源只能同时被一个节点使用,我们可以用创建临时顺序节点的方式实现分布式锁;当我们创建临时节点后,通过 getChildren 获取当前等待锁的全部节点,如果当前节点是所有节点中序号最小的就得到了当前资源的使用权限,在对资源进行处理后,就可以通过删除 /resource/lock-00000000x 来释放锁,如果当前节点不是最小值,就会注册一个 Watcher 等待 /resource 子节点的变化直到当前节点的序列号成为最小值。
 
  在集群中争夺同一资源的服务器特别多的情况下为了减少羊群效应,即每次子节点改变时都会通知当前节点,造成资源的浪费,我们其实可以将 getChildren 换成 getData,让当前节点只监听前一个节点的删除事件。我们减少了每一个服务需要关注的事情,只让它们监听需要关心的数据变更,减少 Zookeeper 发送不必要的通知影响效率。
 
  技术内幕
 
  接下来,我们将从系统模型、序列化与协议、客户端工作原理,会话,服务端工作原理以及数据存储等方面来向读者揭示 ZooKeeper的技术内幕,帮助读者更深入地了解ZooKeeper 这一分布式协调框架。
 
  系统模型
 
  在本节中,我们首先将从数据模型、节点特性、版本、Watcher 和 ACL五方面来讲述ZooKeeper的系统模型。
 
  数据模型
 
  ZooKeeper的视图结构和标准的Unix文件系统非常类似,但没有引入传统文件系统中目录和文件等相关概念,而是使用了其特有的“数据节点”概念,我们称之为 ZNode 。ZNode是 ZooKeeper中数据的最小单元,每个ZNode上都可以保存数据,同时还可以挂载子节点,因此构成了一个层次化的命名空间,我们称之为树。
 
  ZNode 的节点路径标识方式和 Unix 文件系统路径非常相似,都是由一系列使用斜杠/进行分割的路径表示,开发人员可以向这个节点中写入数据,也可以在节点下面创建子节点。
 
  znode
 
  节点特性
 
  在 ZooKeeper中,每个数据节点都是有生命周期的,其生命周期的长短取决于数据节点的节点类型。在 ZooKeeper中,节点类型可以分为持久节点(PERSISTENT)、临时节点(EPHEMERAL)和顺序节点 (SEQUENTIAL)三大类,具体在节点创建过程中,通过组合使用,可以生成以下四种组合型节点类型:
 
  状态信息
 
  每个数据节点除了存储了数据内容外,还存储了数据节点本身的一些状态信息。
 
  node-status
 
  版本信息
 
  ZooKeeper中为数据节点引入了版本的概念,每个数据节点都具有三种类型的版本信息,对数据节点的任何更新操作都会引起版本号的变化。
 
  node-version
 
  在ZooKeeper中,version属性正是用来实现乐观锁机制中的“写入校验”的。
 
  version = setDataRequest.getVersion();
  int currentVersion = nodeRecord.stat.getVersion();
  if(version != -1 && version != currentVersion) {
      throw new KeeperException.BadVersionException(path);
  }
  version = currentVersion + 1;
  Watcher 功能
 
  在ZooKeeper中,引入了Watcher机制来实现这种分布式的通知功能。ZooKeeper允许客户端向服务端注册一个Watcher监听,当服务器的一些指定事件触发了这个Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
 
  watcher
 
  从图中我们可以看到,ZooKeeper的Watcher机制主要包括客户端线程、客户端WatcherManager和ZooKeeper服务器三部分。在具体工作流程上,客户端在向ZooKeeper服务器注册Watcher的同时,会将Watcher对象存储在客户端的WatcherManager中。当ZooKeeper服务器端触发Watcher事件后,会向客户端发送通知,客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑。
 
  Zookeeper 的 Watcher 功能实际上是会出现丢事件的情况的,虽然在进行 getData(), getChildren(), 和 exists() 操作时,可以通过 flag 表示注册 watcher。但是因为这个 watcher 只是一个一次性的触发器,所以,当 Server 推送当前 Change Event 到下次 Client 发起新的 Watcher 之前数据的变化客户端是感知不到的,这就会出现 ABA 问题,即数据从 null 变为 A 时,客户端接收到了通知,在客户端发起下一轮 Watcher 之前,数据变更为 B,又变更会 A,这时候客户端成功注册 Watcher 之后,感知不到数据之前变成过 B。
 
  ACL 功能
 
  ZooKeeper的ACL权限控制和Unix/Linux操作系统中的ACL有一些区别,读者可以从三个方面来理解ACL机制,分别是:权限模式(Scheme)、授权对象(ID)和权限(Permission),通常使用“schemepermission”来标识一个有效的ACL信息。
 
  Scheme 功能
 
  权限模式用来确定权限验证过程中使用的校验策略。在ZooKeeper中,开发人员使用最多的就是以下四种权限模式。
 
  ID
 
  权限赋予的用户或一个指定实体。在不同的权限模式下,授权对象是不同的。
 
  Permission
 
  在ZooKeeper中,所有对数据的操作权限分为以下五大类:
 
  序列化协议
 
  ZooKeeper的客户端和服务端之间会进行一系列的网络通信以实现数据的传输。对于一个网络通信,首先要解决的就是对数据的序列化和反序列化处理,在ZooKeeper中,使用了Jute这一序列化组件来进行数据的序列化和反序列化操作。同时,为了实现一个高效的网络通信程序,良好的通信协议设计也是至关重要的。
 
  通信协议
 
  基于TCP/IP协议,ZooKeeper实现了自己的通信协议来完成客户端与服务端、服务端与服务端之间的网络通信。ZooKeeper通信协议整体上的设计非常简单,对于请求,主要包含请求头和请求体,对于响应,则主要包含响应头和响应体。
 
  客户端
 
  客户端是开发人员使用ZooKeeper最主要的途径,因此我们有必要对ZooKeeper客户端的内部原理进行详细讲解。ZooKeeper的客户端主要由以下几个核心组件组成。
 
  客户端的整个初始化和启动过程大体可以分为以下三个步骤。
 
  会话 Session
 
  会话(Session)是ZooKeeper中最重要的概念之一,客户端和服务端之间的任何交互操作都与会话息息相关,这其中就包括临时节点的生命周期、客户端请求的顺序执行以及Watcher通知机制等。
 
  会话状态
 
  在ZooKeeper客户端和服务端成功完成连接创建后,就建立了一个会话。ZooKeeper会话在整个运行期间的声明周期中,会在不同的会话状态之间进行切换,这些状态一般可以分为CONNECTING、CONNECTED、RECONNECTING、RECONNECTED和CLOSE等。
 
  如果客户端需要与服务端创建一个会话,那么客户端必须提供一个使用字符串表示的服务器地址列表:“host1:port,host2:port,host3:port”。一旦客户端开始创建ZooKeeper对象,那么客户端状态就会变成CONNECTING,同时客户端开始从上述服务器地址列表中逐个选取IP地址来尝试进行网络连接,直到成功连接上服务器,然后将客户端状态变更为CONNECTED。
 
  通常,伴随着网络闪断或是其他原因,客户端和服务器之间的连接会出现断开情况。一旦碰到这种情况,ZooKeeper客户端会自动进行重连操作,同时客户端的状态再次变为CONNECTING,直到重新连接上ZooKeeper服务器后,客户端状态又会再次转变成CONNECTED。因此,在通常情况下,在ZooKeeper运行期间,客户端的状态总是介于CONNECTING和CONNECTED两者之一。
 
  另外,如果出现诸如会话超时、权限检查失败或是客户端主动退出程序等情况,那么客户端的状态就会直接变为CLOSE。
 
  会话创建
 
  Session
 
  Session是ZooKeeper中的会话实体,代表了一个客户端会话。其包含以下4个基础属性:
 
  sessionId
 
  在SessionTracker初始化的时候,会调用initializeNextSession方法来生成一个初始化的sessionID,之后在ZooKeeper的正常运行过程中,会在该sessionID的基础上为每个会话进行分配,其初始化算法如下:
 
  public static long initializeNextSession(long id) {
      long nextSid = 0;
      nextSid = (System.currentTimeMillis() << 24) >> 8;
      nextSid = nextSid | (id << 56);
      return nextSid;
  }
  上面这个方法就是ZooKeeper初始化sessionID的算法,我们一起深入的探究下。从上面的代码片段中,可以看出sessionID的生成大体可以分为以下5个步骤。
 
  简单地讲,可以将上述算法概括为:高8位确定了所在机器,后56位使用当前时间的毫秒进行随机。
 
  SessionTracker
 
  SessionTracker是ZooKeeper服务端的会话管理器,负责会话的创建、管理和清理等工作。可以说,整个会话的生命周期都离不开SessionTracker的管理。每一个会话在SessionTracker内部都保留了三份,具体如下。
 
  创建连接
 
  服务端对于客户端的“会话创建”请求的处理,大体可以分为四大步骤,分别是ConnectRequest请求、会话创建、处理器链路处理和会话响应。
 
  会话管理 分桶策略
 
  ZooKeeper的会话管理主要是由SessionTracker负责的,其采用了一种特殊的会话管理方式,我们称之为“分桶策略”。所谓分桶策略,是指将类似的会话放在同一区块中进行管理,以便于ZooKeeper对会话进行不同区块的隔离处理以及同一区块的统一处理。
 
  ZooKeeper将所有的会话都分配在了不同的区块之中,分配的原则是每个会话的“下次超时时间点”(ExpirationTime)。ExpirationTime是指该会话最近一次可能超时的时间点,对于一个新创建的会话而言,其会话创建完毕后,ZooKeeper就会为其计算ExpirationTime,计算方式如下:
 
  ExpirationTime = CurrentTime + SessionTimeout
 
  在ZooKeeper的实际实现中,Zookeeper的Leader服务器在运行期间会定时的进行会话超时检查,其时间间隔是ExpirationInterval,单位是毫秒,默认值是tickTime的值,即默认情况下,每隔2000毫秒进行一次会话超时检查。为了方便对多个会话同时进行超时检查,完整的ExpirationTime的计算方式如下:
 
  ExpirationTime_ = CurrentTime + SessionTimeout
  ExpirationTime = (ExpirationTime_/ExpirationInterval + 1) * ExpirationInterval
  会话激活
 
  为了保持客户端会话的有效性,在ZooKeeper的运行过程中,客户端会在会话超时时间过期范围内向服务端发送PING请求来保持会话的有效性,我们俗称“心跳检测”。同时,服务端需要不断地接收来自客户端的这个心跳检测,并且需要重新激活对应的客户端会话,我们将这个重新激活的过程称为TouchSession。会话激活的过程,不仅能够使服务端检测到对应客户端的存活性,也能让客户端自己保持连接状态。
 
  超时检测
 
  在ZooKeeper中,会话超时检查同样是由SessionTracker负责的。SessionTracker中有一个单独的线程专门进行会话超时检查,这里我们称其为“超时检查线程”,其工作机制的核心思路非常简单:逐个依次对会话桶中剩下的会话进行清理。
 
  会话清理
 
  当SessionTracker的会话超时检查线程整理出一些已经过期的会话后,那么就要开始进行会话清理了。会话清理的步骤大致可以分为以下七步。
 
  标记会话状态为“已关闭”
 
  为了保证在清理期间不再处理来自该客户端的新请求,SessionTracker会首先将该会话的isClosing属性标记为true。发起“会话关闭”请求
 
  为了使该会话的关闭操作在整个服务端集群中都生效,ZooKeeper使用了提交“会话关闭”请求的方式,并立即交付给PrepRequestProcessor处理器进行处理。收集需要清理的临时节点
 
  在ZooKeeper的内存数据库中,为每个会话都单独保存了一份由该会话维护的所有临时节点集合,因此在会话清理阶段,只需要根据当前即将关闭的会话的sessionID从内存数据库中获取到这份临时节点列表即可。
 
  实际上,有如下细节需要处理:在ZooKeeper处理会话关闭请求之前,正好有以下请求到达了服务端并正在处理中: 添加“节点删除”事务变更
 
  完成该会话相关的临时节点收集后,ZooKeeper会逐个将这些临时节点转换成“节点删除”请求,并放入事务变更队列outstandingChanges中去。删除临时节点
 
  FinalRequestProcessor处理器会触发内存数据库,删除该会话对应的所有临时节点。移除会话
 
  完成节点删除后,需要将会话从SessionTracker中移除。主要就是从上面提到的三个数据结构(sessionById、sessionsWithTimeout和sessionSets)中将该会话移除掉。关闭NIOServerCnxn
 
  最后,从NIOServerCnxnFactory找到该会话对应的NIOServerCnxn,将其关闭。 重连
 
  当客户端和服务端之间的网络连接断开时,ZooKeeper客户端会自动进行反复的重连,直到最终成功连接上ZooKeeper集群中的一台机器。在这种情况下,再次连接上服务端的客户端有可能会处于以下两种状态之一。
 
  当客户端和服务端之间的连接断开后,用户在客户端可能会看到两类异常:CONNECTION_LOSS(连接断开)和SESSION_EXPIRED(会话过期)。
 
  服务器启动
 
  ZooKeeper服务器的启动,大体可以分为以下五个主要步骤:配置文件解析、初始化数据管理器、初始化网络I/O管理器、数据恢复和对外服务。下图是单机版ZooKeeper服务器的启动流程图。
 
  singleton-start
 
  集群版和单机版ZooKeeper服务器启动过程在很多地方是一致的,
 
  leader-follower
 
  选举 Leader选举概述 启动时期的Leader选举
 
  要进行Leader选举的时候,隐式条件便是ZooKeeper的集群规模至少是2台机器,只有一台服务器启动的时候,是无法进行Leader选举的。
 
  每个Server会发出一个投票
 
  初始情况,对于Server1和Server2来说,都会投给自己,每次投票包含的最基本的元素包括:所推举的服务器myid和ZXID。接收来自各个服务器的投票
 
  集群中每个服务器在收到投票后,首先会判断投票的有效性,包含检查是否是本轮投票,是否来自LOOKING状态的服务器。处理投票
 
  在接收到来自其他服务器的投票后,针对每个投票,服务器都需要将别人的投票和自己的投票进行PK,PK的规则如下 统计投票
 
  每次投票后,服务器都会统计所有投票,判断是否已经有过半机器接收到相同的投票信息。改变服务器状态
 
  一旦确定了Leader,每个服务器就会更新自己的状态:如果是Follower,那么就变更为FOLLOWING,如果是Leader,那么就变更为LEADING。 运行期间的Leader选举
 
  在ZooKeeper集群正常运行过程中,一旦选出一个Leader,那么所有服务器的集群角色一般不会再发生变化,不管是非Leader集群挂了还是新机器加入集群,都不会影响Leader。一旦Leader挂了,那么整个集群将暂时无法对外服务,而是进入新一轮的Leader选举。
 
  算法分析
 
  在ZooKeeper中,提供了三种Leader选举的算法,分别是LeaderElection、UDP版本的FastLeaderElection和TCP版本的FastLeaderElection,可以通过在配置文件zoo.cfg中使用electionAlg属性来指定,分别用数字0-3表示。0表示LeaderElection,1表示UDP版本的FastLeaderElection,并且是非授权模式,2表示UDP版本的FastLeaderElection,使用授权模式,3代表TCP版本的FastLeaderElection。从3.4.0版本开始,Zookeeper废弃了0-2这三种算法,只保留了TCP版本的FastLeaderElection选举算法。
 
  术语解释 进入Leader选举
 
  当ZooKeeper集群中的一台服务器出现以下两种情况时,就会开始进入Leader选举
 
  而当一台机器进入Leader选举流程时,当前集群也可能会处于以下两种状态
 
  第一种情况,这种情况通常是某一台服务器启动比较晚,在他启动之前,集群已经可以正常工作。针对这种情况,当该机器试图去选举Leader时,会被告知当前服务器的Leader信息,对于该机器来说,仅仅需要和Leader机器建立起连接,并进行状态同步即可。
 
  下面我们看看集群中不存在Leader的情况下,如何进行Leader选举。
 
  开始第一次投票
 
  通常有两种情况会导致集群中不存在Leader,一种是整个服务器刚刚初始化启动时,另一种情况就是运行期间当前Leader所在的服务器挂了。此时,集群中所有机器都处于LOOKING的状态。当一台服务器处于LOOKING状态时,那么他就会向集群中所有其他机器发送消息,我们称这个消息为“投票”。
 
  在这个投票消息中包含了两个最基本的信息:所推举的服务器SID和ZXIDunix系统内幕,用(SID,ZXID)表示。一般都是投自己。
 
  变更投票
 
  集群中每台机器发出自己的投票后,也会接收到来自集群中其他机器的投票。每台机器都会根据一定的规则,来处理收到的其他机器的投票,并以此来决定是否需要变更自己的投票。这个规则也成了整个Leader选举算法的核心所在。我们首先定义一些术语。
 
  对比过程如下:
 
  假定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)。
 
  结合上面规则,给出下面的集群变更过程。
 
  经过第二轮投票后,集群中的每台机器都会再次接收到其他机器的投票,然后开始统计投票,如果一台机器收到了超过半数的相同投票,那么这个投票对应的SID机器即为Leader。此时Server3将成为Leader。
 
  由上面规则可知,通常那台服务器上的数据越新(ZXID会越大),其成为Leader的可能性越大,也就越能够保证数据的恢复。如果ZXID相同,则SID越大机会越大。
 
  确定Leader
 
  经过这第二次投票后,集群中每台机器都会再次收到其他机器的投票,然后开始统计投票。如果一台机器收到了超过半数的相同的投票,那么这个投票对应的SID机器即为Leader。
 
  通常哪台服务器上的越新,那么越有可能成为Leader,原因很简单,数据越新,ZXID越大,也就越能够保证数据的恢复。
 
  总结一下: ZAB 的选举过程相较于 Paxos 来说,ZAB 更加专注于让 epoch 更高的节点当选,而 epoch 相同时又通过 sid 概念来进行排序,这样能够更加高效地完成选举过程,同时后续的数据恢复过程的工作量也更小。新 leader 只有小概率会从别的节点前一个拉取前一个 leader 的遗留数据,因为新 leader 大概率就是保有数据最多的节点。而 Paxos 则更加宽泛,所有节点没有优先级,大家一起竞争,谁运气好谁就是 leader。
 

(编辑:我爱制作网_池州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章