分布式与一致性协议之ZAB协议(七)

news2025/1/12 3:58:44

ZAB协议

ZAB协议:如何处理读写请求

你应该有这样的体会,如果你想了解一个网络服务,执行的第一个功能肯定是写操作,然后才会执行读操作。比如,你要了解ZooKeeper,那么肯定会在zkClient.sh命令行中执行写操作(比如create /geekbang 123)写入数据,然后再执行读操作(比如get /geekbang)查询数据。这样一来,你才会直观地理解ZooKeeper的使用方法。

在我看来,任何网络服务最重要的功能就是处理读写请求,因为我们访问网络服务的本质就是执行读写操作,ZooKeeper也不例外,对ZooKeeper而言,这些功能更重要,因为如何处理写请求关乎着操作的顺序性,会影响节点的创建;而如何处理读请求关乎着一致性,也影响着客户端是否会读到旧数据。

接下来,会从ZooKeeper系统的角度全面地分析整个读写请求的流程,帮助你更加全面、透彻地理解读写请求背后的原理。
我们都知道,在ZooKeeper中,写请求必须在领导者上处理,如果跟随者接收到写请求,则需要将写请求转发给领导者,当写请求对应的提案被复制到大多数节点上时,领导者会提交提案,并通知跟随者提交提案。而读请求可以在任何节点上处理,也就是说,ZooKeeper实现的是最终一致性。

所以,理解了如何处理读写请求,不仅能理解读写这个最重要功能的核心原理,还能更好地理解ZooKeeper的性能和一致性。这样在实际场景中安装部署ZooKeeper的时候,就能游刃有余地做资源规划了。比如,如果度请求比较多,可以增加节点,如配置5节点集群,而不是常见的3节点集群。

ZooKeeper处理读写请求的原理。

其实前面已经演示"如何实现操作顺序性"时旧已经介绍了ZooKeeper处理读写请求的原理。这里不再赘述,只在前面的基础上补充几点。
首先,在ZooKeeper中,与领导者"失联"的节点是不能处理读写请求的。比如,如果一个跟随者与领导者的连接发生了读超时,那么它会将自己的状态设置为LOOKING,那么此时它既不能转发写请求给领导者处理,也不能处理读请求,只有当它"找到"领导者后,才能处理读写请求.

例子
  • 举个例子,某集群发生分区故障,节点C与节点A(领导者)、节点B断联,那么节点C将设置自己的状态为LOOKING,此时节点C既不能执行读操作,也不能执行写操作。如图所示,在这里插入图片描述
    其次,当大多数节点进入广播阶段后,领导者才能提交提案,因为提案提交需要来自大多数节点的确认。最后写请求只能在领导者节点上处理,所以ZooKeeper集群写性能约等于单机。而读请求可以在所有的节点上处理,所以,读性能是水平扩展的。也就是说,你可以通过分集群的方式来突破写性能的限制,并通过增加更加节点来扩展集群的读性能。

ZooKeeper处理读写请求的代码实现

ZooKeeper处理读写请求的具体流程分析如下。

如何实现写操作

在ZooKeeper代码中,处理写请求的核心流程如图所示。这里我用跟随者接收到写请求的情况演示一下。
在这里插入图片描述

  • 1.跟随者在FollowerRequestProcessor.processRequest()中接收到写请求。具体来说,写请求是系统在ZooKeeperServer.submitRequestNow()中发给跟随者的,如代码所示
firstProcessor.processRequest(si)

而firstProcessor是在FollowerZooKeeperServer.setupRequestProcessors()中创建的,如代码所示

protected void setupRequestProcessors() {
// 创建finalProcessor,提交提案或响应查询
RequestProcessor finalProcessor = new FinalRequestProcessor(this);
// 创建commitProcessor,处理提案提交或读请求
comitProcessor = new CommitProcessor(finalProcessor, Long.toString(getServerId()), true, getZooKeeperServerListener());
commitProcessor.start();
// 创建firstProcessor,接收发给跟随者的请求
firstProcessor = new FollowerRequestProcessor(this,commitProcessor);

((FollowerRequest)firstProcessor).start();
// 创建syncProcessor,将提案持久化存储,并返回确认响应给领导者
syncProcessor = new SyncRequestProcessor(this, new SendAckRequestProcessor(getFollower()));
syncProcessor.start();
}

需要注意的是,跟随者节点和领导者节点的firstProcessor是不同的,这样firstProcessor在ZooKeeperServer.submitRequestNow()中被调用时,就分别进入了跟随者和领导者的代码流程。另外,setupRequestProcessors()创建了两条处理链,如图所示。在这里插入图片描述

  • 2.跟随者在FollowerRequestProcessor.run()中将写请求转发给领导者,如代码所示
// 调用learner.request() 将请求发送给领导者
zks.getFollower().request(request);
  • 3.领导者在LeaderRequestProcessor.processRequest()中接收写请求,并最终调用pRequest()创建事务(也就是提案)并持久化存储,如代码所示
// 创建事务
pRequest2Txn(request.type, zks.getNextZxid(), request, create2Request, true);
......
// 分配事务标识符
request.zxid = zks.getZxid();
// 调用ProposalRequestProcessor.processRequest()处理写请求,并将事务持久化存储
nextProcessor.processRequest(request);

需要注意的是,写请求也是在ZooKeeperServer.submitRequestNow()中发给领导者的,如代码所示

firstProcessor.processRequest(si)

而firstProcessor是在LeaderZooKeeperServer.setupRequestProcessors()中创建的,如代码所所示:

protected void setupRequestProcessors() {
// 创建finalProcessor,最终提交提案和响应查询
RequestProcessor finalProcessor = new FinalRequestProcessor(this);
// 创建toBeAppliedProcessor,存储可提交的提案,并在提交提案后从toBeApplied队列移除已提交的提案
RequestProcessor toBeAppliedProcessor = new Leader.ToBeAppliedRequestProcessor(finalProcessor, getLeader());
// 创建commitProcessor,处理提案提交或读请求
commitProcessor = new COmmitProcessor(toBeAppliedProcessor, Long.toString(getServerId()),false, getZooKeeperServerListener());
commitProcessor.start();
// 创建proposalProcessor,按照顺序广播提案给跟随者
ProposalRequestProcessor proposalProcessor = new ProposalRequestProcessor(this, commitProcessor);
proposalProcessor.initialize();
// 创建preRequestProcessor,根据请求创建提案
preRequestProcessor = new PreRequestProcessor(this, proposalProcessor);
preRequestProcessor.start();
// 创建firstProcessor,接收发给领导者的请求
firstProcessor = new LeaderRequestProcess(this, preRequestProcessor);
...
}

需要注意的是,与跟随者类似,setupRequestProcessor()也为领导者创建了两条处理链(其中处理链2是在创建proposalRequestProcessor时创建的),如图所示.其中,处理链1是核心处理链,最终实现写请求处理(创建提案、广播提案、提交提案)和读请求对应的数据响应。处理链2实现提案持久化存储,并返回确认响应给领导者自己在这里插入图片描述

  • 4.领导者在ProposalRequestProcessor.processRequest()中调用propose()将提案广播给集群所有节点,如代码所示:
zks.getLeader().propose(request);
  • 5.跟随者在Follower.processPacket()中接收到提案,持久化存储,并返回确认响应给领导者,如代码所示
fzk.logRequest(hdr, txn, digest);
  • 6.领导者在接收到大多数节点的确认响应(Leader.processAck())后,最终在CommitProcessor.tryToCommit()提交提案,并广播COMMIT消息给跟随者,如代码所示
// 通知跟随者提交
commit(zxid);
// 自己提交
zk.commitProcessor.commit(p.request);
  • 7.跟随者接收到COMMIT消息后,在FollowerZooKeeperServer.commit()中提交提案,如果最初的写请求是自己接收到的,则返回成功响应给客户端,如代码所示
// 必须顺序提交
long firstElementZxid = pendingTxns.element().zxid;
if (firstElementZxid != zxid) {
LOG.error("Commiting zxid 0x" + Long.toHexString(zxid) + "but next pending txn 0x" + Long.toHexString(firstElementZxid));
ServiceUtils.requestSystemExit(ExitCode.UNMATCHED_TXN_COMMIT.getValue());
}

// 将准备提交的提案从pendingTxns队列移除
Request request = pendingTxns.remove();
request.logLatency(ServiceMetrics.getMetrics().COMMIT_PROPAGRATION_LATENCY);
// 最终调用FinalRequestProcessor.processRequest() 提交提案,如果最初的写请求是自己接收到的,则返回成功响应给客户端
commitProcessor.commit(request);

这样,ZooKeeper就完成了写请求的处理。需要特别注意的是,在分布式系统中,消息或者核心消息的持久化存储很关键,也很重要,因为这是保证集群稳定运行的关键。当然数据写入最终还是为了后续的数据读取,那么ZooKeeper是如何实现读操作的呢?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1654492.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dynamic Extraction of Subdialogues for Dialogue Emotion Recognition

对话情感识别的子对话动态提取 摘要1. 介绍2 相关工作2.1 对话上下文建模2.2 常识知识 3 方法3.1 问题定义3.2 模型概述3.3 特征提取模块3.4 依赖性建模3.5 交互式子对话提取模块3.6 重要性增强的多头自注意力模块3.7 子对话框主题提取模块3.8. 分类模块 四、实验4.1 数据集4.1…

JAVA基础之jsp标准标签

jsp动作标签实现实例化一个实体类 <jsp:useBean id"标识符" class"java类名" scope"作用范围"> 传统的java方式实例化一个实体类 Users user new Users(); <%%> id: 对象名 * class:类 创建对象时,完全限定名(包名…

vue3使用el-autocomplete请求远程数据

服务器端 RestController RequestMapping("/teacher") public class TeacherController {Resourceprivate TeacherService teacherService;GetMapping({"/v1/getTop10TeacherByName/","/v1/getTop10TeacherByName/{name}"})public ResultBean&l…

巡检机器人有哪些功能和作用?

在科技如此发达的时代&#xff0c;巡检机器人犹如一位不知疲倦的守护者&#xff0c;悄然走进了我们的生活。它们具备着令人惊叹的功能和作用&#xff0c;成为了保障安全、提高效率的重要力量。那么&#xff0c;巡检机器人功能和作用&#xff1f;下面我们来说说旗晟机器人的几款…

爬虫:爬取豆瓣电影

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 上篇我们将到如何利用xpath的规则&#xff0c;那么这一次&#xff0c;我们将通过案例来告诉读者如何使用Xpath来定位到我们需要的数据&#xff0c;就算你不懂H5代码是怎么个嵌套或者十分复…

Leetcode—387. 字符串中的第一个唯一字符【简单】

2024每日刷题&#xff08;127&#xff09; Leetcode—387. 字符串中的第一个唯一字符 实现代码 class Solution { public:int firstUniqChar(string s) {int count[26] {0};for(char c: s) {count[c - a];}for(int i 0; i < s.length(); i) {if(count[s[i] - a] 1) {re…

LSTM神经网络 vs Transformer在量化中的应用

LSTM,全称Long Short-Term Memory,是一种特殊的递归神经网络。它通过巧妙的"门"结构,可以有效地捕捉时间序列数据中的长期依赖关系。这一特点,使得LSTM在处理股价这种具有时间序列特性的数据时,展现出了非凡的潜力。 这种特殊的递归神经网络 与一般的前馈神经网络不…

BACnet转MQTT网关智联楼宇json格式自定义

智能建筑的BACnet协议作为楼宇自动化领域的通用语言&#xff0c;正逐步迈向更广阔的物联网世界。随着云计算和大数据技术的飞速发展&#xff0c;如何将BACnet设备无缝融入云端生态系统&#xff0c;成为众多楼宇管理者关注的焦点。本文将以一个实际案例&#xff0c;揭示BACnet网…

Baidu Comate智能编码助手:提升软件生产力的高效工具使用教程

目录 一、前言 二、Comate助手概览 三、核心功能详解 智能推荐与自动补全 生成单元测试 代码注释生成 四、使用场景与优势 五、总结与展望 一、前言 随着信息技术的飞速发展&#xff0c;编程已经成为许多行业不可或缺的一部分。然而&#xff0c;编程过程中的繁琐和重复…

路由策略与路由控制

1.路由控制工具 匹配工具1&#xff1a;访问控制列表 &#xff08;1&#xff09;通配符 当进行IP地址匹配的时候&#xff0c;后面会跟着32位掩码位&#xff0c;这32位称为通配符。 通配符&#xff0c;也是点分十进制格式&#xff0c;换算成二进制后&#xff0c;“0”表示“匹配…

谷歌月球模型

收费产品&#xff0c;白嫖党勿扰 收费金额500元 1 概述 前些时间&#xff0c;有个客户&#xff0c;想fight TAIWAN&#xff0c;于是乎&#xff0c;我把谷歌地球整个台湾的模型都下载下来了&#xff0c;大约300GB。今天&#xff0c;又有个客户&#xff0c;提出一个过分要求&…

Linux网络编程:TCP编程实现

目录 1、前言 2、函数介绍 2.1 socket函数 与 通信域 2.2 bind函数 与 通信结构体 2.2.1 domain通信地址族 与 通信结构体 2.2.2 IPv4地址族结构体 2.2.3 通用地址族结构体 2.2.4 示例&#xff1a;为套接字fd绑定通信结构体addr 2.3 listen函数 与 accept函数 …

KMP + Compose 跨平台 Android IOS 实战入门

KMP&#xff08;Kotlin Multiplatform&#xff09;是一种面向移动端开发的跨平台框架&#xff0c;使用 Kotlin 语言编写&#xff0c;可实现在 Android 和 iOS 平台上共享代码和逻辑。通过 KMP 框架&#xff0c;我们可以编写一次代码&#xff0c;然后在不同的平台上进行部署和运…

HFSS学习-day2-T形波导的优化设计

入门实例–T形波导的内场分析和优化设计 HFSS--此实例优化设计 优化设计要求1. 定义输出变量Power31、Power21、和Power11&#xff0c;表示Port3、Port2、Port1的输出功率2.参数扫描分析添加扫描变量和输出变量进行一个小设置添加输出变量进行扫描分析 3. 优化设计&#xff0c…

Java 中的 HTTP 客户端库OkHttp、Apache HttpClient和HttpUrlConnection

大家好&#xff0c;我是G探险者。 项目开发里面经常会有这么一种场景&#xff1a;与服务器进行 HTTP 通信。一般存在于服务间远程调用的场景 Java 生态系统提供了多种 HTTP 客户端库&#xff0c;每种都有其自己的特点、优势和适用场景。 本文将介绍几种主要的 Java HTTP 客户…

网上离婚,快速离婚,网上立案,视频开庭,没有30天冷静期

☁️网上离婚&#xff0c;可以申请线上立案&#xff0c;视频开庭&#xff0c;这样对于无法回到本地开庭或者出于某种原因双方不想见面的朋友来说是非常便利的。 ☁️线上起诉离婚需提供的材料:&#xff08;根据自身情况选择提供&#xff09; 1&#xff1a;民事起诉状 2&…

神经网络案例实战

&#x1f50e;我们通过一个案例详细使用PyTorch实战 &#xff0c;案例背景&#xff1a;你创办了一家手机公司&#xff0c;不知道如何估算手机产品的价格。为了解决这个问题&#xff0c;收集了多家公司的手机销售数据&#xff1a;这些数据维度可以包括RAM、存储容量、屏幕尺寸、…

项目管理-项目资源管理2/2

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 资源管理&#xff1a;6个过程“硅谷火箭管控” ①规划资源管理&#xff1a; 写计划 ②估算活动资源&#xff1a;估算团队资源&…

2024最新手赚手机软件APP下载排行网站源码及应用商店源码

前言 这是一款简洁蓝色的手机软件下载应用排行、平台和最新发布网站&#xff0c;采用响应式织梦模板。主要包括主页、APP列表页、APP详情介绍页、新闻资讯列表、新闻详情页、关于我们等模块页面。 想入行手赚行业的朋友&#xff0c;这套源码非常适合你&#xff0c;简单的部署上…

JAVA栈相关习题3

1.将递归转化为循环 比如&#xff1a;逆序打印链表 // 递归方式void printList(Node head){if(null ! head){printList(head.next);System.out.print(head.val " ");}} // 循环方式void printList(Node head){if(nullhead){return;}Stack<Node> snew Stack<…