在zookeeper中,follower也可以接收客户端连接,处理客户端请求,本文将分析follower处理客户端请求的流程:

  • 读请求处理
  • 写请求转发与响应

follower接收转发客户端请求

网络层接收客户端数据包

leader、follower都会启动ServerCnxnFactory组件,用来接收客户端连接、读取客户端数据包、将客户端数据包转发给zk应用层。

在"zookeeper源码(08)请求处理及数据读写流程"一文中已经介绍,ServerCnxn在读取到客户端数据包之后,会调用zookeeperServer的processConnectRequest或processPacket方法:

  • processConnectRequest方法:创建session
  • processPacket方法:处理业务请求

processConnectRequest创建session

  • 会使用sessionTracker生成sessionId、创建session对象
  • 生成一个密码
  • 提交一个createSession类型Request并提交给业务处理器
long createSession(ServerCnxn cnxn, byte[] passwd, int timeout) {
// 生成sessionId、创建session对象
long sessionId = sessionTracker.createSession(timeout);
// 生成密码
Random r = new Random(sessionId ^ superSecret);
r.nextBytes(passwd);
// 提交createSession类型Request
CreateSessionTxn txn = new CreateSessionTxn(timeout);
cnxn.setSessionId(sessionId);
Request si = new Request(cnxn, sessionId, 0, OpCode.createSession, RequestRecord.fromRecord(txn), null);
submitRequest(si);
return sessionId;
}

processPacket处理业务请求

  • 封装Request
  • 验证largeRequest
  • 提交业务层处理器
Request si = new Request(cnxn, cnxn.getSessionId(), h.getXid(), h.getType(), request, cnxn.getAuthInfo());
int length = request.limit();
if (isLargeRequest(length)) {
// checkRequestSize will throw IOException if request is rejected
checkRequestSizeWhenMessageReceived(length);
si.setLargeRequestSize(length);
}
si.setOwner(ServerCnxn.me);
submitRequest(si);

FollowerRequestProcessor处理器

在follower端,客户端请求会由FollowerRequestProcessor处理:

  1. 把请求提交下游CommitProcessor处理器
  2. 写请求转发给leader处理
  3. 读请求经过CommitProcessor直接转发给FinalRequestProcessor处理器,直接查询数据返回给客户端
public void run() {
try {
while (!finished) { Request request = queuedRequests.take(); // Screen quorum requests against ACLs first 略 // 转发给CommitProcessor处理器
// 提交到queuedRequests队列
// 写请求还会提交到queuedWriteRequests队列
maybeSendRequestToNextProcessor(request); // ... // 写请求需要转发给leader处理
switch (request.type) {
case OpCode.sync:
zks.pendingSyncs.add(request); // 待同步命令
zks.getFollower().request(request);
break;
case OpCode.create:
case OpCode.create2:
case OpCode.createTTL:
case OpCode.createContainer:
case OpCode.delete:
case OpCode.deleteContainer:
case OpCode.setData:
case OpCode.reconfig:
case OpCode.setACL:
case OpCode.multi:
case OpCode.check:
zks.getFollower().request(request);
break;
case OpCode.createSession:
case OpCode.closeSession:
if (!request.isLocalSession()) {
zks.getFollower().request(request);
}
break;
}
}
} catch (Exception e) {
handleException(this.getName(), e);
}
}

转发leader

zks.getFollower().request(request);

Learner转发请求:

void request(Request request) throws IOException {
// 略 ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream oa = new DataOutputStream(baos);
oa.writeLong(request.sessionId); // sessionId
oa.writeInt(request.cxid); // 客户端xid
oa.writeInt(request.type); // 业务类型
byte[] payload = request.readRequestBytes(); // 请求体
if (payload != null) {
oa.write(payload);
}
oa.close();
// 封装REQUEST数据包
QuorumPacket qp = new QuorumPacket(Leader.REQUEST, -1, baos.toByteArray(), request.authInfo);
writePacket(qp, true); // 通过网络发给leader服务器
}

leader处理follower请求

LearnerHandler接收REQUEST请求

case Leader.REQUEST:
bb = ByteBuffer.wrap(qp.getData());
sessionId = bb.getLong(); // 解析请求信息
cxid = bb.getInt();
type = bb.getInt();
bb = bb.slice();
Request si;
if (type == OpCode.sync) {
si = new LearnerSyncRequest(
this, sessionId, cxid, type, RequestRecord.fromBytes(bb), qp.getAuthinfo());
} else {
si = new Request(null, sessionId, cxid, type, RequestRecord.fromBytes(bb), qp.getAuthinfo());
}
si.setOwner(this); // 用来判断请求来自follower
learnerMaster.submitLearnerRequest(si); // 提交给业务处理器
requestsReceived.incrementAndGet();

submitLearnerRequest提交业务处理器:

public void submitLearnerRequest(Request si) {
zk.submitLearnerRequest(si);
}

LeaderZooKeeperServer提交业务处理器:

public void submitLearnerRequest(Request request) {
// 提交给PrepRequestProcessor处理器
prepRequestProcessor.processRequest(request);
}

从此处开始走leader处理写请求流程。

leader处理写请求流程回顾

  • PrepRequestProcessor - 做事务设置
  • ProposalRequestProcessor - 发起proposal,将Request转发给SyncRequestProcessor写事务log、本地ack
  • CommitProcessor - 读请求直接调用下游处理器,写请求需要等待足够的ack之后commit再调用下游RequestProcessor处理器
  • ToBeAppliedRequestProcessor - 维护toBeApplied列表
  • FinalRequestProcessor - 把事务应用到ZKDatabase,提供查询功能,返回响应

follower处理leader数据

在follower中,Follower使用processPacket方法处理来自leader的数据包,此处看一下PROPOSAL和COMMIT的逻辑。

PROPOSAL数据包

fzk.logRequest(hdr, txn, digest);

logRequest会使用syncProcessor将事务写入到txnlog文件,之后调用SendAckRequestProcessor处理器给leader发ack数据包。

leader收到超过半数的ack之后会发COMMIT数据包让各个节点将事务应用到ZKDatabase中。

COMMIT数据包

fzk.commit(qp.getZxid());

CommitProcessor处理器会将其提交到committedRequests队列,之后客户端Request会继续向下游FinalRequestProcessor处理器传递。

FinalRequestProcessor处理器

  • 把事务应用到ZKDatabase中
  • 提供查询功能
  • 给客户端返回响应

zookeeper源码(09)follower处理客户端请求的更多相关文章

  1. zookeeper源码 — 五、处理写请求过程

    目录 处理写请求总体过程 客户端发起写请求 follower和leader交互过程 follower发送请求给客户端 处理写请求总体过程 zk为了保证分布式数据一致性,使用ZAB协议,在客户端发起一次 ...

  2. zookeeper源码分析之二客户端启动

    ZooKeeper Client Library提供了丰富直观的API供用户程序使用,下面是一些常用的API: create(path, data, flags): 创建一个ZNode, path是其 ...

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

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

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

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

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

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

  6. Zookeeper 源码(三)Zookeeper 客户端源码

    Zookeeper 源码(三)Zookeeper 客户端源码 Zookeeper 客户端主要有以下几个重要的组件.客户端会话创建可以分为三个阶段:一是初始化阶段.二是会话创建阶段.三是响应处理阶段. ...

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

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

  8. Zookeeper 源码(六)Leader-Follower-Observer

    Zookeeper 源码(六)Leader-Follower-Observer 上一节介绍了 Leader 选举的全过程,本节讲解一下 Leader-Follower-Observer 服务器的三种角 ...

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

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

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

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

随机推荐

  1. Kafka学习之四_Grafana监控相关的学习

    Kafka学习之四_Grafana监控相关的学习 背景 想一并学习一下kafaka的监控. 又重新开始学习grafana了: 下载地址: https://grafana.com/grafana/dow ...

  2. [转帖]vCenter使用 VMCA 续订证书:续订证书时发生意外错误

    https://www.dinghui.org/vcenter-sts-certificate.html 起因:有一处客户vCenter告警:STS签名证书即将过期. 处理办法:系统管理-证书-证书管 ...

  3. nginx日志定期备份清理的方法

    nginx日志定期备份清理的方法 前言 实在不想动不动就 yum install 也不太想因为一个很小的需求就搞一下ansible. 想着能够尽量简单, 尽量方便的进行一些工作. 具体思路就是 压缩, ...

  4. [转帖]ipset命令介绍与基本使用

    一. 介绍 ipset命令是用于管理内核中IP sets模块的,如iptables之于netfilter.ipset字面意思是一些IP地址组成一个集合(set).但是ipset用于用于存储IP地址,整 ...

  5. echarts显示地图

    <template> <div class="managingPatientSize"> <div id="china-map"& ...

  6. 离开页面关闭video标签

    <video src="./play.mp4" id="maskmore_1" controls="controls" autopla ...

  7. vue 半场动画进入状态

    <style> .box{ width: 30px; height: 30px; border-radius: 50%; background: red; } </style> ...

  8. pycharm的docstring多了一行type

    注释中多了一行:type 设置为Epytext PyCharm 2020.3.5 (Community Edition) def test(param1,param2,param3): "& ...

  9. word 常用设置

    目录 目录 关闭 Word 句首字母自动大写功能 1 Word 生成目录 1 Word 快速调整标题级别 1 Word 关闭句首字母自动大写功能 参考:https://zhuanlan.zhihu.c ...

  10. C/C++ x32 Inline Hook 代码封装

    Hook 技术常被叫做挂钩技术,挂钩技术其实早在DOS时代就已经存在了,该技术是Windows系统用于替代DOS中断机制的具体实现,钩子的含义就是在程序还没有调用系统函数之前,钩子捕获调用消息并获得控 ...