《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

文章目录

一、从ACID到CAP/BASE

具体可参考:数据库事物和分布式事物:https://blog.csdn.net/jushisi/article/details/112378972

二、一致性协议(2PC、3PC、Paxos算法)

2PC、3PC、Paxos算法,可以说,这三种一致性协议都是非常优秀的分布式一致性协议,都从不同方面不同程度地解决了分布式数据一致性问题,使用范围都非常广泛。

其中二阶段提交协议解决了分布式事务的原子性问题,保证了分布式事务的多个参与者要么都执行成功,要么都执行失败。但是,在二阶段解决部分分布式事务问题的同时,依然存在一些难以解决的诸如同步阻塞、无限期等待和“脑裂”等问题。

三阶段提交协议则是在二阶段提交协议的基础上,添加了PreCommit过程,从而避免了二阶段提交协议中的无限期等待问题。

而Paxos算法引入了“过半”的理念,通俗地讲就是少数服从多数的原则。同时,Paxos算法支持分布式节点角色之间的轮换,这极大地避免了分布式单点的出现,因此Paxos算法既解决了无限期等待问题,也解决了“脑裂”问题,是目前来说最优秀的分布式一致性协议之一。

三、ZooKeeper是什么

ZooKeeper 是一个开源的分布式协调服务。它是一个为分布式应用提供一致性服务的软件,分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。

ZooKeeper 的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

在ZooKeeper中,我们将那些会改变服务器状态的请求称为“事务请求”----通常指的就是那些创建节点、更新数据、删除节点以及创建会话等请求。

ZooKeeper 保证了如下分布式一致性特性

  • 顺序一致性:对于来自客户端的每个更新请求,ZooKeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序。
  • 原子性:所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群所有机器都成功应用了某一个事务,要么都没有应用。
  • 单一视图:无论客户端连接的是哪个ZooKeeper服务器,其看到的服务端数据模型都是一致的。
  • 可靠性:一旦服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来,除非有另一个事务又对其进行了变更。
  • 实时性(最终一致性):这里需要注意的是,Zookeeper仅仅保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。

ZooKeeper的设计目标

  • 简单的数据模型
  • 可以构建集群
  • 顺序访问
  • 高性能

三、ZAB协议

1、ZAB协议

ZooKeeper并没有完全采用Paxos算法,而是使用了一种称为ZooKeeper Atomic Broadcast (ZAB, ZooKeeper原子消息广播协议)的协议作为其数据一致性的核心算法。

ZAB协议是为分布式协调服务ZooKeeper专门设计的一种支持崩溃恢复的原子广播协议。

在ZooKeeper中,主要依赖ZAB协议来实现分布式数据一致性。基于该协议,ZooKeeper实现了一种主备模式的系统架构来保持集群中各副本之间数据的一致性。具体的:

  1. 使用一个单一的主进程(Leader)来接收并处理客户端的所有事务请求(写请求)
  2. 保证一个全局的变更序列被顺序应用。(事务顺序执行)
  3. 当前主进程出现异常的时候,依旧能够正常工作。(Leader选举)

ZAB协议的核心是定义了对于那些会改变ZooKeeper服务器数据状态的事务请求的处理方式,即:

所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务器,而余下的其他服务器则成为Follower服务器。Leader服务器负責将一个客户端事务请求转换成一个事务Proposal (提议),并将该Proposal分发给集群中所有的Follower服务器。之后Leader服务器需要等待所有Follower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前一个Proposal进行提交。

2、ZAB协议的两种模式

ZAB协议包括两种基本的模式,分别是崩溃恢复和消息广播。

当整个服务框架在启动过程中,或是当Leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进入恢复模式并选举产生新的Leader服务器。

当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后,ZAB协议就会退出恢复模式,进入消息广播模式了。

其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致。

(1)消息广播模式

ZAB协议的消息广播过程使用的是一个原子广播协议,类似于一个二阶段提交过程。针对客户端的事务请求,Leader服务器会为其生成对应的事务Proposal,并将其发送给集群中其余所有的机器,然后再分别收集各自的选票,最后进行事务提交,如图4-2所示就是ZAB协议消息广播流程的示意图。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结
在整个消息广播过程中, Leader服务器会为每个事务请求生成对应的Proposal来进行广播,并且在广播事务Proposal之前, Leader服务器会首先为这个事务Proposal分配一个全局单调递增的唯一ID,我们称之为事务ID (即ZXID),由于ZAB协议需要保证每一个消息严格的因果关系,因此必须将每一个事务Proposal按照其ZXID的先后顺序来进行排序与处理。

具体的,在消息广播过程中,
Leader服务器会为每一个Follower服务器都各自分配一个单独的队列,然后将需要广播的事务Proposal依次放入这些队列中去,并且根据FIFO策略进行消息发送。每一个Follower服务器在接收到这个事务Proposal之后,都会首先将其以事务日志的形式写入到本地磁盘中去,并且在成功写入后反馈给Leader服务器个Ack响应。当Leader服务器接收到超过半数Follower的Ack响应后,就会广播一个Commit消息给所有的Follower服务器以通知其进行事务提交,同时Leader自身也会完成对事务的提交,而每一个Follower服务器在接收到Commit消息后,也会完成对事务的提交。

(2)崩溃恢复模式

基本特性

  • ZAB协议需要确保那些已经在Leader服务器上提交的事务最终被所有服务器都提交。
  • ZAB协议需要确保丢弃那些只在Leader服务器上被提出的事务。

根据这两个要求,如果让Leader选举算法能够保证新选举出来的Leader服务器拥有集群中所有机器最高编号(即ZXID最大)的事务Proposal,那么就可以保证这个新选举出来的Leader一定具有所有已经提交的提案。更为重要的是,如果让具有最高编号事务Proposal的机器来成为Leader,就可以省去Leader服务器检查Proposal的提交和丢弃工作的这一步操作了。

在ZAB协议的事务编号ZXID设计中, ZXID是一个64位的数字,其中低32位可以看作是一个简单的单调递增的计数器,针对客户端的每一个事务请求, Leader服务器在产生一个新的事务Proposal的时候,都会对该计数器进行加1操作;而高32位则代表了Leader周期epoch的编号,每当选举产生一个新的Leader服务器,就会从这个Leader服务器上取出其本地日志中最大事务Proposal的ZXID,并从该ZXID中解析出对应的epoch值,然后再对其进行加1操作,之后就会以此编号作为新的epoch,并将低32位置0来开始生成新的ZXID, ZAB协议中的这一通过epoch编号来区分Leader周期变化的策略,能够有效地避免不同的Leader服务器错误地使用相同的ZXID编号提出不一样的事务Proposal的异常情况。

基于这样的策略,当一个包含了上一个Leader周期中尚未提交过的事务Proposal的服务器启动时,其肯定无法成为Leader,原因很简单,因为当前集群中一定包含一个Quorum集合,该集合中的机器一定包含了更高epoch的事务Proposal,因此这台机器的事务Proposal肯定不是最高,也就无法成为Leader了。

四、ZooKeeper

1、数据模型

在谈到分布式的时候,我们通常说的“节点”是指组成集群的每一台机器。然而,在Zookeeper中, “节点”分为两类,第一类同样是指构成集群的机器,我们称之为机器节点。第二类则是指数据模型中的数据单元,我们称之为数据节点ZNode。ZooKeeper将所有数据存储在内存中,数据模型是一棵树(ZNode Tree),由斜杠/进行分割的路径,就是一个Znode,例如/oo/pathl,开发人员可以向这个节点中写入数据,也可以在节点下面创建子节点。ZNode的节点路径标识方式和Unix文件系统路径非常相似。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

2、数据节点

在ZooKeeper中,节点类型可以分为持久节点(PERSISTENT)、临时节点(EPHEMERAL)和顺序节点(SEQUENTIAL)三大类,具体在节点创建过程中,通过组合使用,可以生成以下四种组合型节点类型:

  • 持久节点(PERSISTENT):持久节点是Zookeeper中最常见的一种节点类型。所谓持久节点,是指该数据节点被创建后,就会一直存在于ZooKeeper服务器上,直到有删除操作来主动清除这个节点。
  • 持久顺序节点(PERSISTENT-SEQUENTIAL):持久顺序节点的基本特性和持久节点是一致的,额外的特性表现在顺序性上。在ZooKeeper中,每个父节点都会为它的第一级子节点维护一份顺序,用于记录下每个子节点创建的先后顺序。基于这个顺序特性,在创建子节点的时候,ZooKeeper会自动为给定节点名加上一个数字后缀,作为一个新的、完整的节点名。另外需要注意的是,这个数字后缀的上限是整型的最大值
  • 临时节点(EPHEMERAL):和持久节点不同的是,临时节点的生命周期和客户端的会话绑定在一起。也就是说,如果客户端会话失效,那么这个节点就会被自动清理掉。注意,这里提到的是客户端会话失效,而非TCP连接断开。另外,ZooKeeper规定了不能基于临时节点来创建子节点,即临时节点只能作为叶子节点
  • 临时顺序节点 (EPHEMERAL SEQUENTIAL):临时顺序节点的基本特性和临时节点也是一致的,同样是在临时节点的基础上,添加了顺序的特性。

状态信息
我们提到可以针对ZooKeeper上的数据节点进行数据的写入和子节点的创建。事实上,每个数据节点除了存储了数据内容之外,还存储了数据节点本身的一些状态信息。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

3、版本–保证分布式数据原子性操作

每个数据节点都具有三种类型的版本信息,对数据节点的任何更新操作都会引起版本号的变化,表7-2中对这三类版本信息分别进行了说明。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结
ZooKeeper中的版本概念和传统意义上的软件版本有很大的区别,它表示的是对数据节点的修改次数。我们以其中的version为例来说明。Version表示的是对数据节点数据内容的变更次数,强调的是变更次数,因此即使前后两次变更并没有使得数据内容的值发生变化, version的值依然会变更。

事实上,在ZooKeeper中,version属性正是用来实现乐观锁机制中的“写入校验”的。

4、Watcher机制–数据变更通知

在Zookeeper中,引入了Watcher机制来实现这种分布式的通知功能。ZooKeeper允许客户端向服务端注册一个Watcher监听,当服务端的一些指定事件触发了这个Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。整个Watcher注册与通知过程如图7-4所示。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结
从图7-4中,我们可以看到, ZooKeeper的Watcher机制主要包括客户端线程、客户端WatchManager和ZooKeeper服务器三部分。在具体工作流程上,简单地讲,客户端在向Zookeeper服务器注册Watcher的同时,会将Watcher对象存储在客户端的WatchManager中。当ZooKeeper服务器端触发Watcher事件后,会向客户端发送通知,客户端线程从WatchManager中取出对应的Watcher对象来执行回调逻辑。

Watcher事件
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结
Watcher特性总结

  • 一次性:无论是服务端还是客户端,一旦一个Watcher被触发,Zookeeper都会将其从相应的存储中移除。因此,开发人员在Watcher的使用上要反复注册。这样的设计有效地减轻了服务端的压力。

  • 客户端串行执行:客户端Watcher回调的过程是一个串行同步的过程,这为我们保证了顺序。同时,需要开发人员注意的一点是,千万不要因为一个Watcher的处理逻辑影响了整个客户端的Watcher回调。

  • 轻量: Watcher通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。例如:针对NodeDataChanged事件, ZooKeeper的Watcher只会通知客户端指定数据节点的数据内容发生了变更,而对于原始数据以及变更后的新数据都无法从这个事件中直接获取到,而是需要客户端主动重新去获取数据----这也是ZooKeeper的Watcher机制的一个非常重要的特性。

    另外,客户端向服务端注册Watcher的时候,并不会把客户端真实的Watcher对象传递到服务端,仅仅只是在客户端请求中使用boolean类型属性进行了标记,同时服务端也仅仅只是保存了当前连接的ServerCnxn对象。

    如此轻量的Watcher机制设计,在网络开销和服务端内存开销上都是非常廉价的。

客户端注册Watcher

在创建一个ZooKeeper客户端对象实例时,可以向构造方法中传入一个默认的Watcher。

public Zookeeper (String connectString, int sessionTimeout, Watcher watcher);

这个Watcher将作为整个ZooKeeper会话期间的默认Watcher,会一直被保存在客户端ZKWatchManagerdefaultWatcher中。

另外,ZooKeeper客户端也可以通过getDatagetChildrenexist三个接口来向ZooKeeper服务器注册Watcher,无论使用哪种方式,注册Watcher的工作原理都是一致的。

5、ACL–保障数据的安全

如何有效地保障ZooKeeper中数据的安全,从而避免因误操作而带来的数据随意变更导致的分布式系统异常非常重要。ZooKeeper提供了一套完善的ACL (Access Control List)权限控制机制来保障数据的安全。

ACL,即访问控制列表,是一种相对来说比较新颖且更细粒度的权限管理方式,可以针对任意用户和组进行细粒度的权限控制。

权限模式: Scheme

权限模式用来确定权限验证过程中使用的检验策略。在ZooKeeper中,开发人员使用最多的就是以下四种权限模式。

  1. IP:从 IP 地址粒度进行权限控制
  2. Digest:最常用,用类似于 username:password 的权限标识来进行权限配置,便于区分不同应用来进行权限控制
  3. World:最开放的权限控制方式,是一种特殊的 digest 模式,只有一个权限标识“world:anyone”
  4. Super:超级用户

6、客户端会话

客户端

ZooKeeper的客户端主要由以下几个核心组件组成。

  • Zookeeper实例:客户端的入口。
  • ClientWatchManager:客户端Watcher管理器。
  • HostProvider:客户端地址列表管理器。
  • ClientCnxn:客户端核心线程,其内部又包含两个线程,即SendThread和EventThread。前者是一个I/0线程,主要负责ZooKeeper客户端和服务端之间的网络I/0通信;后者是一个事件线程,主要负责对服务端事件进行处理。

客户端的整个初始化和启动过程大体可以分为以下3个步骤:

  1. 设置默认Watcher。
  2. 设置ZooKeeper服务器地址列表。
  3. 创建ClientCnxn。

Chroot 客户端隔离命名空间

3.2.0 版本后,添加了 Chroot 特性,该特性允许每个客户端为自己设置一个命名空间。如果一个客户端设置了 Chroot,那么该客户端对服务器的任何操作,都将会被限制在其自己的命名空间下。

通过设置 Chroot,能够将一个客户端应用于 Zookeeper 服务端的一颗子树相对应,在那些多个应用公用一个 Zookeeper 进群的场景下,对实现不同应用间的相互隔离非常有帮助。

会话 Session

一个客户端会话包含以下4个基本属性:

  1. sessionlD:会话ID,用来唯一标识一个会话
  2. TimeOut:会话超时时间。
  3. TickTime:下次会话超时时间点。
  4. isClosing:该属性用于标记一个会话是否已经被关闭

会话管理

ZooKeeper的会话管理主要是由SessionTracker负责的,其采用了“分桶策略”的会话管理方式。所谓分桶策略,是指将类似的会话放在同一区块中进行管理,以便于Zookeeper对会话进行不同区块的隔离处理以及同一区块的统一处理,分配的原则是每个会话的“下次超时时间点” (ExpirationTime),如图7-24所示。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

7、服务器角色

在Zookeeper集群中,分别有Leader,Follower和Observer三种类型的服务器角色。

Leader

Leader服务器是整个Zookeeper集群工作机制中的核心,其主要工作有以下两个。

  • 事务请求的唯一调度和处理者,保证集群事务处理的顺序性。
  • 集群内部各服务器的调度者。

请求处理链

使用责任链模式来处理每一个客户端请求是ZooKeeper的一大特色。Leader服务器的请求处理链如图7-36所示。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

  • PrepRequestProcessor(检查、识别事务请求)
    PrepRequestProcessor是Leader服务器的请求预处理器,也是Leader服务器的第一个请求处理器。PrepRequestProcessor能够识别出当前客户端请求是否是事务请求。对于事务请求,PrepRequestProcessor处理器会对其进行一系列预处理。

  • ProposalRequestProcessor(发起投票,记自己记事物日志)
    ProposalRequestProcessor处理器是Leader服务器的事务投票处理器,也是Leader服务器事务处理流程的发起者。对于非事务请求, ProposalRequestProcessor会直接将请求流转到CommitProcessor处理器,不再做其他处理;而对于事务请求,除了将请求交给CommitProcessor处理器外,还会根据请求类型创建对应的Proposal提议,并发送给所有的Follower服务器来发起一次集群内的事务投票。同时,ProposalRequestProcessor还会将事务请求交付给SyncRequestProcessor进行事务日志的记录。

  • SyncRequestProcessor(记事物日志)
    SyncRequestProcessor是事务日志记录处理器,该处理器主要用来将事务请求记录到事务日志文件中去,同时还会触发Zookeeper进行数据快照。

  • AckRequestProcessor(事物日志记录反馈Ack)
    AckRequestProcessor处理器是Leader特有的处理器,其主要负责在SyncRequestProcessor处理器完成事务日志记录后,向Proposal的投票收集器发送ACK反馈,以通知投票收集器当前服务器已经完成了对该Proposal的事务日志记录。

  • CommitProcessor(等待投票结果提交proposal)
    CommitProcessor是事务提交处理器。对于非事务请求,该处理器会直接将其交付给下一级处理器进行处理;而对于事务请求, CommitProcessor处理器会等待集群内针对 Proposal的投票直到该Proposal可被提交。

  • ToBeCommitProcessor(对可提交的事物形成队列)
    ToBeCommitProcessor处理器中有一个toBeApplied队列,专门用来存储那些已经被CommitProcessor处理过的可被提交的Proposal。ToBeCommitProcessor处理器将这些请求逐个交付给FinalRequestProcessor处理器进行处理----等到FinalRequestProcessor处理器处理完之后,再将其从toBeApplied队列中移除。

  • FinalRequestProcessor(消费队列,写入事物数据)
    FinalRequestProcessor是最后一个请求处理器。该处理器主要用来进行客户端请求返回之前的收尾工作,包括创建客户端请求的响应,针对事务请求,该处理器还会负责将事务应用到内存数据库中去。

Follower

从角色名字上可以看出,Follower服务器是ZooKeeper集群状态的跟随者,其主要工作有以下三个。

  • 处理客户端非事务请求,转发事务请求给Leader服务器。
  • 参与事务请求Proposal的投票。
  • 参与Leader选举投票。

和Leader服务器一样,Follower也同样使用了采用责任链模式组装的请求处理链来处理每一个客户端请求,其请求处理链如图7-37所示。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

  • FollowerRequestProcessor
    FollowerRequestProcessor是Follower服务器的第一个请求处理器,其主要工作就是识别出当前请求是否是事务请求。如果是事务请求,那么Follower就会将该事务请求转发给Leader服务器,Leader服务器在接收到这个事务请求后,就会将其提交到请求处理链,按照正常事务请求进行处理。

  • SendAckRequestProcessor
    SendAckRequestProcessor处理器承担了事务日志记录反馈的角色,在完成事务日志记录后,会向Leader服务器发送ACK消息以表明自身完成了事务日志的记录工作。Follower服务器的SendAckRequestProcessor和Leader服务器AckRequestProcessor作用一样。两者的唯一区别在于,AckRequestProcessor处理器和Leader服务器在同一个服务器上,因此它的ACK反馈仅仅是一个本地操作;而SendAckRequestProcessor处理器由于在Follower服务器上,因此需要通过以ACK消息的形式来向Leader服务器进行反馈。

Observer

该服务器充当了一个观察者的角色,其观察ZooKeeper集群的最新状态变化并将这些状态变更同步过来。

Observer服务器在工作原理上和Follower基本是一致的,对于非事务请求,都可以进行独立的处理;而对于事务请求,则会转发给Leader服务器进行处理。

和Follower唯一的区别在于,Observer不参与任何形式的投票,包括事务请求Proposal的投票和Leader选举投票。简单地讲,Observer服务器只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。

另外,Observer的请求处理链路和Follower服务器也非常相近,如图7-38所示。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

8、Leader选举

4种服务器状态

  • LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
  • FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。
  • LEADING:领导者状态。表明当前服务器角色是 Leader。
  • OBSERVING:观察者状态。表明当前服务器角色是 Observer。

Leader选举流程

  1. 每个Server会发出一个投票。
    初始情况,每个服务器都会将作为Leader服务器来进行投票,每次投票包含的最基本的元素包括:所推举的服务器的myidZXID,我们以(myid, ZXID)的形式来表示。
  2. 接收来自各个服务器的投票。
    每个服务器都会接收来自其他服务器的投票。
  3. 处理投票。
    在接收到来自其他服务器的投票后,针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK的规则如下。
    • 优先检查ZXID, ZXID比较大的服务器优先作为Leader。
    • 如果ZXID相同的话,那么就比较myid,myid比较大的服务器作为Leader服务器。
  4. 统计投票。
    每次投票后,服务器都会统计所有投票,判断是否已经有过半的机器接收到相同的投票信息。
  5. 改变服务器状态。
    一旦确定了Leader,每个服务器就会更新自己的状态:如果是Follower,那么就变更为FOLLOWING;如果是Leader,那么就变更为LEADING。

9、数据与存储

  • 内存数据:即ZooKeeper节点数据。
  • 事物日志:dataDir这个目录是ZooKeeper中默认用于存储事务日志文件的,其实在ZooKeeper中可以为事务日志单独分配一个文件存储目录: dataLogDir
  • snapshot----数据快照:数据快照是ZooKeeper数据存储中另一个非常核心的运行机制。顾名思义,数据快照用来记录ZooKeeper服务器上某一个时刻的全量内存数据内容,并将其写入到指定的磁盘文件中。
    和事务日志类似,ZooKeeper的快照数据也使用特定的磁盘目录进行存储,读者也可以通过dataDir属性进行配置。

数据同步

ZooKeeper集群数据同步通常分为四类,分别是直接差异化同步(DIFF同步)先回滚再差异化同步(TRUNC+DIFF同步)仅回滚同步(TRUNC同步)全量同步(SNAP同步)

在初始化阶段,Leader服务器会优先初始化以全量同步方式来同步数据。当然。这并非最终的数据同步方式。另外会根据Leader和Learner服务器之间的数据差异情况来决定最终的数据同步方式。

选完Leader以后,zk就进入状态同步过程
1、Leader等待server连接;
2、Follower连接leader,将最大的zxid发送给leader;
3、Leader根据follower的zxid确定同步点;
4、完成同步后通知follower 已经成为uptodate状态;
5、Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。
《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

五、ZooKeeper的命令

  • 连接客户端

    进入[zookeeper目录]/bin
    
    sh zkcli.sh -server ip:port
    
  • 创建节点

    create [-s] [-e] path data acl
    其中, -s或-e分别指定节点特性:顺序或临时节点。默认情况下,即不添加-s或-e参数的,创建的是持久节点。
    
  • 获取子节点

    第一次部署的ZooKeeper集群,默认在根节点“/"下面有一个叫作/zookeeper的保留节点。

    ls path [watch]
    
  • 获取节点的数据内容和属性信息

    get path [watch]
    
  • 更新节点

    set path data [version]
    
  • 删除节点

    只能删除叶子节点。要想删除某一个指定节点,该节点必须没有子节点存在。

    delete path [version]
    

六、ZooKeeper典型应用场景

命名服务

命名服务是指通过指定的名字来获取资源或者服务的地址,利用zk创建一个全局的路径,即是唯一的路径,这个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,或者一个远程的对象等等。

配置管理

程序分布式的部署在不同的机器上,将程序的配置信息放在zk的znode下,当有配置发生改变时,也就是znode发生变化时,可以通过改变zk中某个目录节点的内容,利用watcher通知给各个客户端,从而更改配置。

集群管理

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

Leader选举

主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用 Zookeeper 可以协助完成这个过程;

分布式锁

有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。

对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过create znode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。

对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。

参考中的ZooKeeper实现方式:分布式锁

分布式队列

两种类型的队列:
1、同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。
2、队列按照 FIFO 方式进行入队和出队操作。

第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。

第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。在特定的目录下创建PERSISTENT_SEQUENTIAL节点,创建成功时Watcher通知等待的队列,队列删除序列号最小的节点用以消费。此场景下Zookeeper的znode用于消息存储,znode存储的数据就是消息队列中的消息内容,SEQUENTIAL序列号就是消息的编号,按序取出即可。由于创建的节点是持久化的,所以不必担心队列消息的丢失问题。

Dubbo应用

《从Paxos到ZooKeeper分页式一致性原理与实践》------ 总结

  • /dubbo:这是Dubbo在ZooKeeper上创建的根节点。

  • /dubbolcom.foo.BarService:这是服务节点,代表了Dubbo的一个服务。

  • /dubbolcom.foo.BarService/providers:这是服务提供者的根节点,其子节点代表了每,一个服务的真正提供者。

  • /dubbo/com.foo.BarService/consumers:这是服务消费者的根节点,其子节点代表了每一个服务的真正消费者。

  • 服务提供者
    服务提供者在初始化启动的时候,会首先在ZooKeeper的/dubbo/com.foo.BarService/providers节点下创建一个子节点,并写入自己的URL地址,这就代表了"com.fooBarService"这个服务的一个提供者。

  • 服务消费者
    服务消费者会在启动的时候,读取并订阅ZooKeeper上/dubbo/com.foo.BarService/providers节点下的所有子节点,并解析出所有提供者的URL地址来作为该服务地址列表,然后开始发起正常调用。同时,服务消费者还会在ZooKeeper的/dubbo/com.foo.BarService/consumers节点下创建一个临时节点,并写入自己的URL地址,这就代表了"com.foo.BarService"这个服务的一个消费者。

  • 监控中心
    监控中心是Dubbo中服务治理体系的重要一部分,其需要知道一个服务的所有提供者和订阅者,及其变化情况。因此,监控中心在启动的时候,会通过ZooKeeper的/dubbo/com.foo.BarService节点来获取所有提供者和消费者的URL地址,并注册Watcher来监听其子节点变化。

另外需要注意的是,所有提供者在Zookeeper上创建的节点都是临时节点,利用的是临时节点的生命周期和客户端会话相关的特性,因此一旦提供者所在的机器出现故障导致该提供者无法对外提供服务时,该临时节点就会自动从ZooKeeper上删除,这样服务的消费者和监控中心都能感知到服务提供者的变化。

上一篇:MicroServices(七)--分布式一致性算法--Paxos


下一篇:JVM 调优以及问题排查笔记