Zookeeper 源码(六)Leader-Follower-Observer

上一节介绍了 Leader 选举的全过程,本节讲解一下 Leader-Follower-Observer 服务器的三种角色。经过 Leader 选举后各服务器都能确定自己的角色,下一步就是初始化各自的角色。

先回顾一下【QuorumPeer】的 run 方法选举结束后创建对应的角色:

case OBSERVING:
setObserver(makeObserver(logFactory));
observer.observeLeader();
break;
case FOLLOWING:
setFollower(makeFollower(logFactory));
follower.followLeader();
break;
case LEADING:
// 3.1 初始化 Leader 对象
setLeader(makeLeader(logFactory));
// 3.2 lead 线程在这里阻塞
leader.lead();
setLeader(null);
break;

一、Leader

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

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

(1) Leader 初始化 【Leader】

protected Leader makeLeader(FileTxnSnapLog logFactory) throws IOException {
return new Leader(this, new LeaderZooKeeperServer(logFactory, this, this.zkDb));
} // Leader 启动时启动 2888 的端口,用于服务器内部通信(如数据同步等)
Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException {
this.self = self;
try {
if (self.getQuorumListenOnAllIPs()) {
ss = new ServerSocket(self.getQuorumAddress().getPort());
} else {
ss = new ServerSocket();
}
ss.setReuseAddress(true);
if (!self.getQuorumListenOnAllIPs()) {
ss.bind(self.getQuorumAddress());
}
} catch (BindException e) {
// 省略...
}
this.zk = zk;
}

(2) lead 【Leader】

void lead() throws IOException, InterruptedException {
// 1. 恢复本地数据
zk.loadData(); // 2. 启动 lead 端口的监听线程,专门用来监听新的 follower
cnxAcceptor = new LearnerCnxAcceptor();
cnxAcceptor.start(); // 3. 启动服务
startZkServer(); while (true) {
// 省略...
}
}

(3) LearnerCnxAcceptor 【Leader】

follower 连上 leader 后,此时 leader 会为每个 follower 启动单独 IO 线程,请看 LearnerCnxAcceptor 代码

class LearnerCnxAcceptor extends ZooKeeperCriticalThread {
private volatile boolean stop = false;
@Override
public void run() {
while (!stop) {
Socket s = ss.accept();
// 读超时设为 initLimit 时间
s.setSoTimeout(self.tickTime * self.initLimit);
s.setTcpNoDelay(nodelay);
// 为每个 follower 启动单独线程,处理 IO
LearnerHandler fh = new LearnerHandler(s, Leader.this);
fh.start();
}
}

LearnerHandler 持有 Leader 与 Learner 的 Socket 对象,专门用来处理服务器之间的通信。

(4) startZkServer 【Leader】

private synchronized void startZkServer() {
// 省略...
zk.startup();
// 省略...
}

(4) startup 【LeaderZooKeeperServer】

LeaderZooKeeperServer 继承自 ZooKeeperServer,重写了 startup 和 setupRequestProcessors,这里重点关注 Leader 服务器的请求处理链。

@Override
public synchronized void startup() {
super.startup();
if (containerManager != null) {
containerManager.start();
}
} /**
* Leader 服务器的请求处理链
* LeaderRequestProcessor -> PrepRequestProcessor -> ProposalRequestProcessor ->
* CommitProcessor -> ToBeAppliedRequestProcessor -> FinalRequestProcessor
*/
@Override
protected void setupRequestProcessors() {
RequestProcessor finalProcessor = new FinalRequestProcessor(this);
RequestProcessor toBeAppliedProcessor = new Leader.ToBeAppliedRequestProcessor(finalProcessor, getLeader());
commitProcessor = new CommitProcessor(toBeAppliedProcessor,
Long.toString(getServerId()), false,
getZooKeeperServerListener());
commitProcessor.start(); ProposalRequestProcessor proposalProcessor = new ProposalRequestProcessor(this, commitProcessor);
proposalProcessor.initialize();
prepRequestProcessor = new PrepRequestProcessor(this, proposalProcessor);
prepRequestProcessor.start();
firstProcessor = new LeaderRequestProcessor(this, prepRequestProcessor); setupContainerManager();
}

(1) PrepRequestProcessor 请求预处理器。在Zookeeper中,那些会改变服务器状态的请求称为事务请求(创建节点、更新数据、删除节点、创建会话等),PrepRequestProcessor能够识别出当前客户端请求是否是事务请求。对于事务请求,PrepRequestProcessor处理器会对其进行一系列预处理,如创建请求事务头、事务体、会话检查、ACL检查和版本检查等。

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

(3) CommitProcessor 事务提交处理器。对于非事务请求,该处理器会直接将其交付给下一级处理器处理;对于事务请求,其会等待集群内针对Proposal的投票直到该Proposal可被提交,利用CommitProcessor,每个服务器都可以很好地控制对事务请求的顺序处理。

(4) ToBeCommitProcessor 该处理器有一个toBeApplied队列,用来存储那些已经被CommitProcessor处理过的可被提交的Proposal。其会将这些请求交付给FinalRequestProcessor处理器处理,待其处理完后,再将其从toBeApplied队列中移除。

(5) FinalRequestProcessor 用来进行客户端请求返回之前的操作,包括创建客户端请求的响应,针对事务请求,该处理还会负责将事务应用到内存数据库中去。

(6) SyncRequestProcessor 事务日志记录处理器。用来将事务请求记录到事务日志文件中,同时会触发Zookeeper进行数据快照。

(7) AckRequestProcessor 负责在SyncRequestProcessor完成事务日志记录后,向Proposal的投票收集器发送ACK反馈,以通知投票收集器当前服务器已经完成了对该Proposal的事务日志记录。

二、Follower

Follower 的初始化与 Leader 类似,重点关注一下 Follower 的请求处理链。

void followLeader() throws InterruptedException {
// 省略...
try {
InetSocketAddress addr = findLeader();
try {
connectToLeader(addr);
// 省略...
syncWithLeader(newEpochZxid);
QuorumPacket qp = new QuorumPacket();
while (self.isRunning()) {
readPacket(qp);
processPacket(qp);
}
} catch (Exception e) {
// 省略...
}
}
} /**
* Follower 服务器的请求处理链
* 1. FollowerRequestProcessor -> CommitProcessor -> FinalRequestProcessor
* 2. SyncRequestProcessor -> SendAckRequestProcessor
*/
@Override
protected void setupRequestProcessors() {
RequestProcessor finalProcessor = new FinalRequestProcessor(this);
commitProcessor = new CommitProcessor(finalProcessor,
Long.toString(getServerId()), true, getZooKeeperServerListener());
commitProcessor.start();
firstProcessor = new FollowerRequestProcessor(this, commitProcessor);
((FollowerRequestProcessor) firstProcessor).start();
syncProcessor = new SyncRequestProcessor(this,
new SendAckRequestProcessor((Learner)getFollower()));
syncProcessor.start();
}

(1) FollowerRequestProcessor 其用作识别当前请求是否是事务请求,若是,那么Follower就会将该请求转发给Leader服务器,Leader服务器是在接收到这个事务请求后,就会将其提交到请求处理链,按照正常事务请求进行处理。

(2) SendAckRequestProcessor其承担了事务日志记录反馈的角色,在完成事务日志记录后,会向Leader服务器发送ACK消息以表明自身完成了事务日志的记录工作。

参考:

  1. 《Zookeeper源码分析之六 Leader/Follower初始化》:https://blog.csdn.net/haihongazar/article/details/52709244
  2. 《zookeeper源码分析之五服务端(集群leader)处理请求流程》:https://www.cnblogs.com/davidwang456/p/5004599.html
  3. 从 Paxos 到 Zookeeper : 分布式一致性原理与实践

每天用心记录一点点。内容也许不重要,但习惯很重要!

Zookeeper 源码(六)Leader-Follower-Observer的更多相关文章

  1. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  2. Zookeeper 源码(五)Leader 选举

    Zookeeper 源码(五)Leader 选举 前面学习了 Zookeeper 服务端的相关细节,其中对于集群启动而言,很重要的一部分就是 Leader 选举,接着就开始深入学习 Leader 选举 ...

  3. Zookeeper 源码分析-启动

    Zookeeper 源码分析-启动 博客分类: Zookeeper   本文主要介绍了zookeeper启动的过程 运行zkServer.sh start命令可以启动zookeeper.入口的main ...

  4. Zookeeper 源码(四)Zookeeper 服务端源码

    Zookeeper 源码(四)Zookeeper 服务端源码 Zookeeper 服务端的启动入口为 QuorumPeerMain public static void main(String[] a ...

  5. Zookeeper源码(启动+选举)

    简介 关于Zookeeper,目前普遍的应用场景基本作为服务注册中心,用于服务发现.但这只是Zookeeper的一个的功能,根据Apache的官方概述:"The Apache ZooKeep ...

  6. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  7. Zookeeper 源码(七)请求处理

    Zookeeper 源码(七)请求处理 以单机启动为例讲解 Zookeeper 是如何处理请求的.先回顾一下单机时的请求处理链. // 单机包含 3 个请求链:PrepRequestProcessor ...

  8. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  9. 如何编译Zookeeper源码

    1. 安装Ant Ant下载地址:http://ant.apache.org/bindownload.cgi 解压即可. 2. 下载Zookeeper源码包 https://github.com/ap ...

随机推荐

  1. Java之DelayQueue实际应用

    在学习Java 多线程并发开发过程中,了解到DelayQueue类的主要作用:是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其 中的对象只能在其到期时才能从队列中取走 ...

  2. C#修改注册表

    某次需要使用C#对注册表进行操作,不过却发现没有权限,研究了以下发现是当前系统用户的问题.除非当前系统用户是Administrator,否则就会给你抛出一个异常.后来在网上发现了一个方法,原来C#也可 ...

  3. [CF321E]Ciel and Gondolas&&[BZOJ5311]贞鱼

    codeforces bzoj description 有\(n\)个人要坐\(k\)辆车.如果第\(i\)个人和第\(j\)个人同坐一辆车,就会产生\(w_{i,j}\)的代价. 求最小化代价.\( ...

  4. HDU5033 Building(单调栈)

    题意是说在水平轴上有很多建筑物(没有宽度),知道每个建筑物的位置与高度.有m个查询,每次查询位置x所能看到的天空的角度. 方法是将建筑与查询一起排序,从左往右计算一遍,如果是建筑物,则比较最后两个(当 ...

  5. Reusing & Composing GraphQL APIs with GraphQL Bindings

    With GraphQL bindings you can embed existing GraphQL APIs into your GraphQL server. In previous blog ...

  6. graphql 文档 docker 镜像

    因为一些原因 graphql 的官方文档无法查看,后者查看不能是方便,所以在官方github 的文档基础上添加了容器构建, 方便进行查看,对于公司内部使用学习会比较好 原理 很简单,openresty ...

  7. CSS为英文和中文字体分别设置不同的字体

    font-family的调用方法: div { font-family:Arial,'Times New Roman','Microsoft YaHei',SimHei; font:bold 12px ...

  8. oracle 脚本创建数据库的相关文章,教程,源码

    学步园推荐专题: 关于oracle 脚本创建数据库的相关文章 文章标题 文章链接 文章简介 oracle命令行创建数据库的示例脚本 http://www.xuebuyuan.com/964527.ht ...

  9. 【转】嵌入式Linux文件系统启动脚本及分析

    原文网址:http://www.linuxidc.com/Linux/2011-03/33728.htm 在内核初始化完成后,嵌入式linux 文件系统的启动过程主要包含以下几个步骤: 1. 执行/s ...

  10. 【转】Bash Shell中命令行选项/参数处理

    原文网址:http://www.cnblogs.com/FrankTan/archive/2010/03/01/1634516.html 0.引言 写程序的时候经常要处理命令行参数,本文描述在Bash ...