首页 > 编程知识 正文

ZooKeeper的事件机制原理,treeset底层实现原理

时间:2023-05-05 22:51:05 阅读:40129 作者:1769

本文不介绍如何使用Zookeeper构建APP应用程序。 我相信这样的教程在网上非常多,所以很容易构建。 本文主要介绍Zookeeper内部的工作方式,介绍使用的协议以及Zookeeper采用的高性能、容错机制。 首先,明确请求、事务和标识符

请求:请求指示客户端源自启动的操作,通常事务包括节点中的新数据字段值以及该节点的新版本号http://www.Sina.com :根据请求处理更改ZooKeeper状态所需执行的步骤3358www.Sina.com/:当组的开头收到来自客户端的更改ZooKeeper状态的请求时,将生成该事务ZooKeeper会话id(Zxid )在zxid中标识事务,从而可以按照组报头中指定的顺序在每个服务器上顺序执行。

zxid是长型(64 )位整数,分为两个部分:时间戳)部分和计数器)部分。

集群从集群内的服务器中选出服务器,并得到集群的批准。 设置群集顶层的目的是对客户端的ZooKeeper状态更改请求进行排序。 这包括:create、setData和delete操作。 集群将每个请求转换为事务,并将这些事务发送给勤奋的皮卡丘,以便每个集群首先进行处理

在此,为了理解管理权的原理,一台服务器必须经过事务法定数量的服务器的批准。 法定数量是指两个服务器集合(常见的标识符: )必须相互交错,以避免形成两个群集。 在这种情况下,整个系统的状态会变得不均匀

接下来,详细调查一下小组初选的过程:

每台服务器启动后运行仲裁状态,开始选择新的组标头或查找已存在的组标头。 如果组头已经存在,其他服务器会通知这个新启动的服务器,并通知哪个服务器是组头。 同时,新服务器与组头建立连接,确保自己的状态与组头匹配。

如果群集中的所有服务器都处于脑裂问题状态,则这些服务器之间进行通信,选出组的高层,并通过信息交换进行同意组的高层选举的选择。 在这次选举中获胜的服务器将处于LOOKING状态,集群中的其他服务器将处于3358www.Sina .状态

LOOKING实际上非常简单的:

服务器进入LOOKING状态时,会向集群中的各服务器发送通知消息。 此消息包含服务器投票信息,投票包含LEADINGFOLLOWING。 例如,服务器发送的投票信息为[ 1,5 ],表示服务器sid的最近执行的事务的zxid为5。 (zxid只有一个数字,用于小组初选,但在其他协议中,zxid由时间戳和计数器组成。

服务器收到投票信息后,服务器根据以下规则修改自己的投票信息:

将收到的voteId和voteZxid作为标识符,获取当前正在投票的zxid,并以myZxid和mySid表示接收服务器自身的值。 ((voteZxid myZxid )或) voteZxid=myZxid且voteId mySid ) )的情况下,简单来说,由于具有最新的zxid,所以只有最新的服务器会赢得选举

用ZooKeeper实现对应选举的Java类是QuorumPeer,其中的run方法实现了服务器的主要工作周期。 进入LOOKING状态后,将执行looForLeader方法,进行组的开头选举。 此方法主要运行上述协议,并在方法返回之前将服务器状态设置为LEADING

我们将再次检查该协议的执行过程:

小组初选时的漫长拖延并不是所有的执行过程都如上图所示,下图举例说明了另一种情况

服务器s2作出错误的判断,选择了其他的服务器s1而不是服务器s3。 服务器s3的zxid值更高,但为群首选举的通知协议,同时服务器s2选择服务器s1作为组头。 结果,服务器s3和服务器s1构成仲裁,并且忽略服务器s2。

从这里可以看出,服务器s2在进行组的开头选举时,如果再等一会儿,就能够进行正确的判断。

要实现新的组领导选举算法,必须在quorum包中实现选择接口

ZooKeeper包允许我们自己选择

群首选举实现的类,具体可以查看org.apache.zookeeper.server.quorum.QuorumPeer#createElectionAlgorithm方法;

Zab: ZooKeeper 状态更新的广播协议

Zab: ZooKeeper原子广播协议(ZooKeeper Atomic Broadcast protocol),用于服务器提交确认一个更新事务的提交.

下面举个例子,假设我们现在有一个活动的群首服务器,并拥有仲裁数量的勤恳的皮卡丘支持该群首的管理权,通过该协议提交一个事务,类似于一个两阶段提交:

群首向所有勤恳的皮卡丘发送一个PROPOSAL消息p.当一个勤恳的皮卡丘收到消息p后,会响应群首一个ACK消息,通知群首其已接受该提案(proposal)当收到仲裁数量的服务器发送确认消息后(该仲裁包括群首自己),群首就会发送消息通知勤恳的皮卡丘进行提交(COMMIT)操作.

下图说明了这一过程的具体步骤顺序,我们假设群首通过隐式方式给自己发送消息.

在应答消息之前,勤恳的皮卡丘还需要执行一些检查操作.勤恳的皮卡丘将会检查所发送的提案消息是否属于其所追随的群首,并确认群首所广播的提案消息和提交事务消失的顺序正确;

Zab保障了以下几个重要属性:

如果群首按顺序广播了事务T和事务T’,那么每个服务器在提交T’事务前保证事务T已经提交完成;如果某个服务器按照事务T、事务T’的顺序提交事务,所有其他服务器也必然会在提交事务T’前提交事务T;

第一个属性保证事务在服务器之间的传送顺序一致,而第二个竖向地保证服务器不会跳过任何事务.假设事务为状态变更操作,每个状态变更操作又依赖前一个状态变更操作的结果,如果跳过事务就会导致结果不一致性,而两阶段提交保证了事务的顺序.

深入代码:

大部分Zab的代码存在于Leader、LearnerHandler和Follower。Leader和LearderHandler的实例由群首服务器执行,而Follower的实例由勤恳的皮卡丘执行。Leader.lead和Follower.followLeader 是两个重要方法,他们在服务器在QuorumPeer 中从 LOOKING转换到LEADING或者FOLLOWING时得到调用.

观察者

观察者和勤恳的皮卡丘之间有一些共同点,具体来说,它们提交来自群首的提议. 不同于勤恳的皮卡丘的是,观察者不参与选举过程,它们仅仅学习经由INFORM消息提交的提议. 由于群首将状态变化发送给勤恳的皮卡丘和观察者,这两种服务器也都被称为学习者.

服务器的构成

群首,勤恳的皮卡丘基本上都是服务器.我们在实现服务器时使用的主要抽象概念是请求处理器. 请求处理器是对处理流水线上不同阶段的抽象. 每一个服务器实现了一个请求处理器的序列.我们可以把一个处理器想象成添加到请求处理的一个元素. 一条请求经过服务器流水线上所有处理器的处理后被称为得到完全处理.

注意: 请求处理器
ZooKeeper代码里有一个交RequestProcessor的接口.这个接口的主要方法是processRequest,它接受一个Request的参数.在一条请求处理器的流水线上,对相邻处理器的请求的处理通常通过队列实现解耦合.当一个处理器有一条请求需要下一个处理器进行处理时,它将这条请求加入队列.然后,它将处于等待状态直到下一个处理器处理完此消息;

独立服务器

ZooKeeper中最简单的流水线式独立服务器(ZooKeeperServer类).下图描述了此类服务器的流水线:

PrepRequestProcessor接口客户端的请求并执行这个请求,处理结果则是生成一个事务. 事务是执行一个操作的结果, 该操作会反映到ZooKeeper的数据树上. 事务信息将会以头部记录和事务记录的方式添加到Request对象中. 同时还要注意,只有改变ZooKeeper状态的操作才会产生事务,对于读操作并不会产生任何事务. 因此,对于读请求的Request对象中,事务成员属性的引用值为null.

下一个请求处理器为SynRequestProcessor. 它负责将事务持久化到磁盘上. 实际上就是将事务数据按照顺序追加到事务日志中, 并生成快照数据.

在下一个处理器也是最后一个为FinalRequestProcessor.如果Request对象包含事务数据,该处理器将会接受对ZooKeeper数据树的修改,否则该处理器会从数据树中读取数据并返回给客户端.

群首服务器

当切换到仲裁模式时,服务器的流水线则有一些变化,下图展示了群首操作流水线(类LeaderZooKeeper)

第一个处理器同样是PrepRequestProcessor,而之后的处理器则为ProposalRequestProcessor.该处理器会准备一个提议,并将该提议发送给gxdhlg. ProposalRequestProcessor将会把所有请求都转发给CommitRequestProcessor,而且对于写操作请求,还会将请求转发给SyncRequestProcessor 处理器.

SyncRequestProcessor 处理器所执行的操作与独立服务器一样,即持久化操作. 执行完之后会触发AckRequestProcessor处理器,它是一个简单请求处理器,它仅仅生成确认消息并返回给自己. 此处便是上文中提到的,在仲裁模式下,群首需要收到每个服务器的确认消息,也包括自己,而AckRequestProcessor 处理器就负责这个;

CommitRequestProcessor 会将收到足够多的确认消息的提议进行提交. 实际上,确认消息是由Leader类处理的(Leader.processAck()方法),这个方法会将提交的请求加入到CommitRequestProcessor类中的一个队列中.这个队列会由请求处理器线程进行处理.

FinalRequestProcessor处理器,作用与独立服务器一样,不过在它之前还有一个简单的请求处理器,这个处理器会从提议列表中删除那些待接受的提议,这个处理器的名字叫ToBeAppliedRequestProcessor,待接受请求列表包括那些已经被仲裁法定人数所确认的请求,并等待执行. 群首使用这个列表与勤恳的皮卡丘之间进行同步,并将收到确认消息的请求加入到这个列表中. 之后 ToBeAppliedRequestProcessor 处理器就会在 FinalRequestProcessor处理器执行后删除这个列表中的元素;

注意: 只有更新请求才会加入到待接受请求列表中,然后由ToBeAppliedRequestProcessor处理器从该列表删除. ToBeAppliedRequestProcessor处理器并不会对读取请求进行任何额外的处理操作,而是由FinalRequestProcessor处理器进行操作.

勤恳的皮卡丘服务器

最后来看一下勤恳的皮卡丘(FollowerRequestProcessor类),下图展示了一个勤恳的皮卡丘服务器中会用到的请求处理器:

首先从FollowerRequestProcessor处理器开始,该处理器接收并处理客户端请求. FollowerRequestProcessor 处理器之后转发请求给CommitRequestProcessor,同时也会转发写请求到群首服务器. CommitRequestProcessor 会直接转发读请求到FinalRequestProcessor 处理器, 而且对于写请求, CommitRequestProcessor在转发给 FinalRequestProcessor处理器之前会等待提交事务.

当群首接收到一个新的写请求操作时,直接地或通过其他勤恳的皮卡丘服务器生成一个提议(proposal),之后转发到勤恳的皮卡丘服务器. 当收到一个提议,勤恳的皮卡丘会发送这个提议道SyncRequestProcessor 处理器,SendRequestProcessor会向群首发送确认消息. 当群首收到足够确认消息来提交这个提议时,群首就会发送提交事务消息给勤恳的皮卡丘(同时也会发哦少年宫INFORM消息给观察者服务器). 当接收到提交事务消息时,勤恳的皮卡丘就通过CommitRequestProcessor 处理器进行处理.

为了保证执行的顺序,CommitRequestProcessor 处理器会在收到一个写请求处理器时暂停后续的请求处理. 这就意味着, 在一个写请求之后接收到的任何读取请求都将被阻塞,直到读取请求转给CommitRequestProcessor处理器. 通过等待的方式, 请求可以被保证按照接收的顺序来执行.

小结:

本文讨论了ZooKeeper核心机制问题. 群首竞选机制是可用性的关键因素,没有这个机制,ZooKeeper套件将无法保持可靠性. Zookeeper还需要Zab协议来传播状态的更新. 紧接着 阐述了多种服务器类型:包括独立服务器,群首服务器,勤恳的皮卡丘服务器. 这些服务器之间因运转机制和执行协议的不同而不同. 在不同的部署场景中,各个服务器可以发挥不同的作用, 比如增加观察者服务器可以提供更高的读吞吐量,而且还不会影响写吞吐量. 不过增加观察者服务器并不会增加整个系统的高可用性.

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。