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. FastAdmin 的前端环境怎么安装?

    FastAdmin 的前端环境怎么安装? 安装 Git 安装 Node.js 安装 cnpm 安装 bower 开始安装 FastAdmin 的前端组件 bower install bower upd ...

  2. 提高Android Studio的Build速度实践

    1.在下面的目录创建gradle.properties文件 /home/<username>/.gradle/ (Linux) /Users/<username>/.gradl ...

  3. oracle数据库启动时出现ORA-01157和ORA-01110问题

    sql>startup mount; sql>alter database open; RA-01157: 无法标识/锁定数据文件 10 - 请参阅 DBWR 跟踪文件ORA-01110: ...

  4. linux下启动springboot服务

    错误日志 SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder] . __ ...

  5. 单链表LRU

    单链表实现lru 越靠近链表尾部的节点是越早之前访问的 当有一个新的数据被访问时,从链表头开始顺序遍历链表 1.如果此数据之前已经被缓存在链表中 遍历得到这个数据对应的节点,并将其从原来的位置删除,然 ...

  6. MySQL 多表关联更新及删除

    目录: <MySQL中的两种临时表> <MySQL 多表关联更新及删除> <mysql查询优化之三:查询优化器提示(hint)> 一.      多表关联更新 问题 ...

  7. 十一.jQuery源码解析之.pushStack()

    pushStack()顾明思意,就是像桟中添加东西呗,现在看看他是如何添加东西的. 创建一个空的jQuery对象,然后把Dom元素集合放入这个jQuery对象中, 并保留对当前jQuery对象的引用. ...

  8. 让IE依据HTML头标签选择显示模式

    文件兼容性用于定义让IE如何编译你的网页.此文件解释文件兼容性,如何指定你网站的文件兼容性模式以及如何判断一个网页该使用的文件模式. 前言 为了帮助确保你的网页在所有未来的IE版本都有一致的外观,IE ...

  9. leetcode414

    public class Solution { public int ThirdMax(int[] nums) { ; var list = nums.Distinct().OrderByDescen ...

  10. InnoDB: The log sequence number in ibdata files does not match

    InnoDB: The log sequence number in ibdata files does not matchInnoDB的:在ibdata文件的日志序列号不匹配 可能ibdata文件损 ...