群⾸、追随者和观察者根本上都是服务器。我们在实现服务器时使⽤的主要抽象概念是请求处理器。请求处理器是对处理流⽔线上不同阶段的抽象。每⼀个服务器实现了⼀个请求处理器的序列。我们可以把⼀个处理器想象成添加到请求处理的⼀个元素。⼀条请求经过服务器流⽔线上所有处理器的处理后被称为得到完全处理。
注意:请求处理器
ZooKeeper代码里有⼀个叫RequestProcessor的接⼝。这个接⼝的主要⽅法是processRequest,它接受⼀个Request参数。在⼀条请求处理器的流⽔线上,对相邻处理器的请求的处理通常通过队列现实解耦合。当⼀个处理器有⼀条请求需要下⼀个处理器进⾏处理时,它将这条请求加⼊队列。然后,它将处于等待状态直到下⼀个处理器处理完此消息。
5.1 独⽴服务器
Zookeeper中最简单的流⽔线是独⽴服务器(ZeeKeeperServer类)。图9-6描述了此类服务器的流⽔线。它包含三种请求处理器:
- PrepRequestProcessor、
- SyncRequestProcessor
- FinalRequestProcessor。
图9-6:⼀个独立服务器的流⽔线
PrepRequestProcessor接受客户端的请求并执⾏这个请求,处理结果则是⽣成⼀个事务。我们知道事务是执⾏⼀个操作的结果,该操作会反映到ZooKeeper的数据树上。事务信息将会以头部记录和事务记录的⽅式添加到Request对象中。同时还要注意,只有改变ZooKeeper状态的操作才会产⽣事务,对于读操作并不会产⽣任何事务。因此,对于读请求的Request对象中,事务的成员属性的引⽤值则为null。
下⼀个请求处理器为SyncRequestProcessor。SyncRequestProcessor负责将事务持久化到磁盘上。实际上就是将事务数据按顺序追加到事务⽇志中,并⽣成快照数据。对于硬盘状态的更多细节信息,我们将在下⼀节进⾏讨论。
下⼀个处理器也是最后⼀个为FinalRequestProcessor。如果Request对象包含事务数据,该处理器将会接受对ZooKeeper数据树的修改,否则,该处理器会从数据树中读取数据并返回给客户端。
5.2 群⾸服务器
当我们切换到仲裁模式时,服务器的流⽔线则有⼀些变化,⾸先我们介绍群⾸的操作流⽔线(类LeaderZooKeeper),如图9-7所⽰。
image图9-7:群首服务器的流⽔线
第⼀个处理器同样是PrepRequestProcessor,⽽之后的处理器则为ProposalRequestProcessor。该处理器会准备⼀个提议,并将该提议发送给跟随者。ProposalRequestProcessor将会把所有请求都转发给CommitRequestProcessor,⽽且,对于写操作请求,还会将请求转发给SyncRequestProcessor处理器。
SyncRequestProcessor处理器所执⾏的操作与独⽴服务器中的⼀样,即持久化事务到磁盘上。执⾏完之后会触发AckRequestProcessor处理器,这个处理器是⼀个简单请求处理器,它仅仅⽣成确认消息并返回给⾃⼰。我们之前曾提到过,在仲裁模式下,群⾸需要收到每个服务器的确认消息,也包括群⾸⾃⼰,⽽AckRequestProcessor处理器就负责这个。
在ProposalRequestProcessor处理器之后的处理器为CommitRequestProcessor。CommitRequestProcessor会将收到⾜够多的确认。消息的提议进⾏提交。实际上,确认消息是由Leader类处理的(Leader.processAck()⽅法),这个⽅法会将提交的请求加⼊到CommitRequestProcessor类中的⼀个队列中。这个队列会由请求处理器线程进⾏处理。
下⼀个处理器也是最后⼀个为FinalRequestProcessor处理器,它的作⽤与独⽴服务器⼀样。FinalRequestProcessor处理更新类型的请求,并执⾏读取请求。在FinalRequestProcessor处理器之前还有⼀个简单的请求处理器,这个处理器会从提议列表中删除那些待接受的提议,这个处理器的名字叫ToBeAppliedRequestProcessor。待接受请求列表包括那些已经被仲裁法定⼈数所确认的请求,并等待被执⾏。群⾸使⽤这个列表与追随者之间进⾏同步,并将收到确认消息的请求加⼊到这个列表中。之后ToBeAppliedRequestProcessor处理器就会在FinalRequestProcessor处理器执⾏后删除这个列表中的元素。
注意,只有更新请求才会加⼊到待接受请求列表中,然后由ToBeAppliedRequest-Processor处理器从该列表移除。
ToBeAppliedRequestProcessor处理器并不会对读取请求进⾏任何额外的处理操作,⽽是由FinalRequestProcessor处理器进⾏操作。
5.3 追随者和观察者服务器
现在我们来讨论追随者(FollowerRequestProcessor类),图9-8展⽰了⼀个追随者服务器中会⽤到的请求处理器。我们注意到图中并不是⼀个单⼀序列的处理器。⽽且输⼊也有不同形式:
- 客户端请求、
- 提议、
- 提交事务。
我们通过箭头来将标识追随者处理的不同路径。
image图9-8:追随者服务器的流⽔线
我们⾸先从FollowerRequestProcessor处理器开始,该处理器接收并处理客户端请求。FollowerRequestProcessor处理器之后转发请求给CommitRequestProcessor,同时也会转发写请求到群⾸服务器。
CommitRequestProcessor会直接转发读取请求到FinalRequestProcessor处理器,⽽且对于写请求,CommitRequestProcessor在转发给FinalRequestProcessor处理器之前会等待提交事务。
当群⾸接收到⼀个新的写请求操作时,直接地或通过其他追随者服务器来⽣成⼀个提议,之后转发到追随者服务器。当收到⼀个提议,追随者服务器会发送这个提议到SyncRequestProcessor处理器,SendRequestProcessor会向群⾸发送确认消息。当群⾸服务器接收到⾜够确认消息来提交这个提议时,群⾸就会发送提交事务消息给追随者(同时也会发送INFORM消息给观察者服务器)。当接收到提交事务消息时,追随者就通过CommitRequestProcessor处理器进⾏处理。
为了保证执⾏的顺序,CommitRequestProcessor处理器会在收到⼀个写请求处理器时暂停后续的请求处理。这就意味着,在⼀个写请求之后接收到的任何读取请求都将被阻塞,直到读取请求转给CommitRequestProcessor处理器。通过等待的⽅式,请求可以被保证按照接收的顺序来被执⾏。
对于观察者服务器的请求流⽔线(ObserverZooKeeperServer类)与追随者服务器的流⽔线⾮常相似。但是因为观察者服务器不需要确认提议消息,因此观察者服务器并不需要发送确认消息给群⾸服务器,也不⽤持久化事务到硬盘。对于观察者服务器是否需要持久化事务到硬盘,以便加速观察者服务器的恢复速度,这样的讨论正在进⾏中,因此对于以后的ZooKeeper版本也会会有这⼀个功能。
网友评论