Redis Cluster Gossip Protocol: PING, PONG, MEET

返回目录

PING / PONG / MEET 的发送

过程

  1. 计算freshNodes。freshNodes表示在消息中能携带的,在cluster节点字典中的节点总数,但需要减去myself和对端节点,因为myself的信息会存储在消息头中。实际上,并非所有在cluster节点字典中的节点都需要带出去,对于那些处于handshake,disconnected状态的节点是不用考虑的。
    freshNodes = size(cluster nodes) - 2
  2. 计算wanted。wanted表示准备在消息中携带的gossip数。个数不能少于3,不能超过freshNodes。
    wanted = floor(size(cluster nodes) / 10)
    wanted = max(wanted, 3)
    wanted = min(wanted, freshNodes)
  3. 获取pfailWanted。pfailWanted表示处于PFAIL状态的节点数。
  4. 如果当前link不是inbound,而且消息类型为PING,则更新对端实体的pingSent(发送ping的时间)为当前时间。
  5. 构建消息头部
  6. 计算maxIterations。maxIterations表示在遍历cluster节点字典时的最大遍历次数。
    maxIterations = wanted * 3
  7. 编写gossip
gossipCount = 0
while freshNodes > 0  gossipCount < wanted && maxIterations:从cluster节点字典中随机抽取一个node /* 注意:是随机地抽取 */if node是myself || 处于PFAIL状态: /* myself会在消息头,PFAIL会在最后追加 */continueif (node处于handshake或者NOADDR(地址不可知)) ||(node还没有outbound link && node没有slots):freshNodes--	/* 节省CPU */continueif node已被添加到gossip:continue把node添加到gossipfreshNodes--gossipCount++
  1. 如果pfailCount > 0,则把PFAIL节点追加到gossip
while cluster节点字典还没遍历完 and pfailWanted > 0:获取一个nodeif (node处于handshake或者NOADDR) || node并非PFAIL:	/* 只追加PFAIL的节点 */continuegossipCount++pfailWanted--
  1. 如果配置了cluster-announce-hostname,则
    • 给消息的mflags添加EXT_DATA标记,表示消息附带extension
    • 把cluster-announce-hostname写入pingExtension
    • 把pingExtension追加到消息末尾
  2. 计算消息的总长度
  3. 设置消息的count = gossipCount
  4. 设置消息的extensions = extension的个数
  5. 设置消息的totalLen = 上面计算出的总长度
  6. 发送消息
    - PING和MEET通过outbound link发送
    - PONG通过inbound link发送
gossip内容

每个要加入到gossip中的节点都会在其中生成一个对应条目,包含的信息:

  1. 节点ID
  2. 最近一次ping此节点的时间(如果已经收到此节点的PONG,会重置为0)
  3. 最近一次收到此节点PONG的时间
  4. 节点IP
  5. 节点的port,pport,cport
  6. 节点的flags

PING / PONG / MEET的接收处理

过程

第1 ~ 3步是涵盖所有类型的消息。

  1. 合法性检查
    • 消息类型(type)的检查
    • 消息长度检查。有的消息会携带gossip,有的会附带extension,所以消息的总长度是可变的,需要结合消息头部的元数据和消息的内容进行检查。
  2. 根据link和消息头,从cluster节点字典中查找实体sender
if link关联了node && node不是处于handshake:sender = node
else:sender = 根据消息头中的sender,从cluster节点字典中查找实体if sender存在 && link没有关联node:/* inbound link在创建时还不知道节点的真实ID,所以会找不到sender,因此要到达这里才能关联上sender */把link设置为sender的inbound link/* 关联后,下次就可以从link获取sender,而不需要再从cluster节点字典查找了 */把sender关联到link
  1. 通用处理,适用于所有消息类型
if sender存在:/* 更新数据接收时间,以免因为sender一直在发送数据而误认为它timeout */sender的dataReceived = 当前时间if sender没有处于handshake:/* 更新currentEpoch和configEpoch */if 消息头的currentEpoch > server的currentEpoch:server的currentEpoch = 消息头的currentEpochif 消息头的configEpoch > sender的configEpoch:sender的configEpoch = 消息头的configEpoch更新sender的复制偏移和相应时间if server正在做manual failover && myself是sender的slave &&消息头的mflags包含PAUSED && server的mf_master_offset == -1:更新server的mf_server_offset = sender的复制偏移
  1. 处理PING,MEET消息
if 消息类型是PING或者MEET:/* 获取myself的IP */if (消息类型是MEET || myself还没有IP) && 没有配置cluster-announce-ip:myself的IP = 从socket中获取自己的IP/* 在cluster节点字典中创建实体 */if sender不存在 && 消息类型为MEET:为sender创建node实体,生成随机ID/* 获取sender的IP和ports */if 消息头的myip不为全0:node的ip = myipelse:node的ip = 从socket获取对端的ip把消息头的port,pport, cport复制到node把node加入cluster节点字典/* 解析gossip */if sender不存在 && 消息类型为MEET:处理消息的gossip回复PONG消息给对端节点
  1. 处理PING,PONG,MEET消息
if 消息类型是PING或者PONG或者MEET:node = link关联的nodeif link是outbound:if node处于handshake:/* sender已经代表了对端节点,接下去node的ID也会被更新成跟sender的一样,同一个ID不能对应2个实体,所以需要把node删掉 */if sender存在: 检查ip和ports的变化,更新sender /* Point-1 */从cluster节点字典中删除node /* 会释放node的inbound和outbound link*/return更新node的ID = 消息头的sender /* 随机ID -> 真实的ID */从node中删除HANDSHAKE标记 /* 表示handshake过程结束 */根据消息头的flags,把node设置为master或slaveelse if node的ID != 消息头的sender:/* 可能对端变更了ID,也可能是由于网络的变化连错了节点 */标记node为NOADDR重置node的ip,port,pport,cport为0释放linkreturn/* 检查NOFAILOVER,更新sender */if sender存在:	/* 我们假定对端发来的信息是最新的,所以直接更新.NOFAILOVER对应的是cluster-replica-no-failover配置项 */if 消息头的flags带了NOFAILOVER:标记sender为NOFAILOVERelse:移除sender的NOFAILOVER/* 如果ip和各个端口发生了变化,则需要更新 */if sender存在 && 消息类型为PING && sender没有处于handshake:检查ip和ports的变化,更新sender/* 如果我们收到了PONG,需要清除PFAIL和FAIL状态 */if link是outbound && 消息类型为PONG:更新node的pongReceived = 当前时间重置node的pingSent = 0 if node处于PFAIL:清除PFAIL状态	/* 简单地移除PFAIL标记	*/else if node处于FAIL:清除node的FAIL状态 /* Point-2 *//* 检查是否发生了角色切换: slave -> master or master -> slave */if sender存在:if 消息头的slaveof为全0: /* 对端现在是master */把sender转换为master	 /* Point-3: 如果sender原本就是master,就无需转换 */else:	/* 对端现在是slave */if sender需要从master变为slave:把sender转换为slave	/* Point-4 */if sender的master发生了变化: 把sender从旧master迁移到新master /* Point-5 *//* 更新slots */if sender是master && sender的slots跟消息头的myslots存在不同:重新绑定消息头的myslots到sender /* Point-6 *//* 如果sender声称slot属于它,但事实并非如此,则需要通知sender */if sender存在 && sender的slots跟消息头的myslots存在不同:查找消息头的myslots中是否存在这样一个slot:1. 在myself看来,它不属于sender2. 它的configEpoch > 消息头的configEpochif 存在这样的slot:发送UPDATE消息给sender/* 解决configEpoch冲突 */		if sender是master && myself也是master &&消息头的configEpoch == myself的configEpoch:if myself的ID < sender的ID:	/* 字符串比较 */myself的configEpoch = (++server的currentEpoch)保存配置/* 处理gossip */		if sender存在:处理消息中的gossip  /* Point-7 *//* 处理消息中的pingExtension */if 消息头的extensions > 0: /* 消息带有extension *//* 当前只有一种extension: pingExtension */sender的hostname = pingExtension中的hostname 

Points 解释

Point-1:检查ip和ports的变化,更新sender
/* 检查是否使用同一条连接,若是,则不可能有变化 */
if link跟sender的link一样:	return	
/* 检查IP和ports是否有变化 */
if sender的ports跟消息头的ports相同 &&: /* port, pport, cport */sender的ip跟消息头的myip相同: /* 如果myip为全0,则使用socket上的对端IP */return
更新sender的IP和ports
if sender的link存在: /* IP和ports变更了,需要重新建立连接 */释放掉sender的link	/* 释放后会使用新的IP和port自动重连 */
if sender是myself的master:更新server复制时使用的IP和port,重新开启复制
Point-2:清除node的FAIL状态
/* 对于slave节点,只要连接上,都会认为它恢复了;对于没有slot的master节点,只要连接上,就不需要在看它是否fail了足够长的时间 */
if sender是slave || sender没有slots:清除它的FAIL标记/* 对于master节点,如果它fail的时间足够长,而且还有slots属于它,说明它没有被failover,现在连接上了,则可以认为它恢复了 */
if sender是master && sender有slots &&当前时间 - sender的failTime > 2 * cluster-node-timeout:清除它的FAIL标记
Point-3:把sender转换为master
解除sender和oldMaster之间的关联
if oldMaster没有其他slave:清除oldMaster的MIGRATE_TO标记 
if sender != myself:添加MIGRATE_TO标记到sender
设置sender为master
Point-4:把sender转换为slave
把原本属于sender的slots,全部设置为没有owner
清除sender的MIGRATE_TO标记
设置sender为slave
Point-5:把sender从原本的master迁移到新的master
解除sender和oldMaster之间的关联
if oldMaster没有其他slave:清除oldMaster的MIGRATE_TO标记
建立sender和newMaster之间的关联
添加MIGRATE_TO标记到newMaster
Point-6:重新绑定消息头的myslots到sender
if sender == myself:  /* 不需要对自己进行更新 */return
if myself是master:currentMaster = myself
elsecurrentMaster = myself的master
newMaster = null
migratedOurSlots = 0
遍历消息头中的myslots:跳过那些已经属于sender的slot跳过处于importing状态的slot/* 如果slot没有owner,或者sender声称slot属于它 */if slot没有owner || slot的configEpoch < 消息头的configEpoch:if slot的owner == myself && slot上有key: /* slot是myself的,需要标识它是dirty */记录slot到dirtySlotsif slot的owner == currentMaster:/* 存在slot从currentMaster迁移到sender */newMaster = sender++migratedOurSlots /* 记录迁移的slot数 */把slot从旧的owner删除把slot添加到senderif newMaster != null &&		/* 存在slot从currentMaster迁移到sender */currentMaster没有slot了 && (cluster-allow-replica-migration ||	/* 配置了允许副本迁移 */消息头的myslots数量 == migratedOurSlots): /* myslots全部从currentMaster迁移到sender *//* 如果myself是master,当前没有slot,说明myself被failover,它需要成为newMaster的副本;如果myself是slave,它的master没有slot,说明它当前没有slot可以复制,myself需要成为myslots的副本 *//* sender成为myself的master */if myself是master:清除它的MIGRATE_TO标记设置它为slave清除它的那些处于importing和migrating的slotselse:解除它跟oldMaster之间的关联把myself的master设置为sender更新server复制时使用的IP和port,重新开启复制重置server的manual failover状态
else if dirtySlots数量 > 0:/* 消息表明这些slots跟我们没有关系了,但是我们还有key在上面,为了维持key和slot之间的一致性,需要清除掉这些key */遍历dirtySlots,删除每个slot上所有的key
Point-7:处理消息中的gossip
遍历消息中的每个gossip:根据gossip的nodename,从cluster节点字典中查找nodeif node存在:  /* 找到关联这个gossip的node */if sender是master && node != myself:if gossip表示node处于PFAIL或FAIL: /* 对端节点连接不上node */把sender发起的failureReport添加到node /* 如果已经存在,则更新时间 *//* 检查node是否需要标记为FAIL */neededQuorum = clusterSize / 2 + 1 /* 计算仲裁所需要的票数 */if node处于PFAIL状态 && /* 我们无法连接上它 */node没有处于FAIL状态: /* 没FAIL才需要处理 */清除那些已经失效的failureReport /* 时间超过了 2 * cluster-node-timeout */failures = 有效的failureReport数if myself是master:	/* 如果我们是master,我们也需要投票 */++failuresif falures >= neededQuorum: /* 多数通过, FAIL成立 */把node从PFAIL转换成FAIL,并设置failTime为当前时间广播FAIL消息到其他节点,迫使它们标记此node已FAILelse: /* sender能连接上node */从node上删除sender发起的failureReport /* 如果有的话 */清除那些已经失效的failureReport/* 检查是否需要更新node的pongReceived */if gossip表示node没有处于PFAIL或FAIL && /* 在对端节点看来,node没有PFAIL或FAIL */node的pingSent == 0 &&	/* 我们没有处于等待回复的ping */node上没有failureReport:	 /* 在我们看来,node没有FAIL */if gossip的pongReceived > node的pongReceived &&	/* 对端节点收到node的PONG比我们更新 */gossip的pongReceived < 当前时间 + 500ms:	/* 各个节点的时钟可能不同步,500ms是容错 */		node的pongReceived = gossip的pongReceived/* 检查IP和ports是否发生了变化 */	if node原本处于PFAIL或FAIL &&	/* 我们连接不上它 */但对端节点能连接上node &&	我们和对端节点拿到的node IP,port和cport不一样:释放掉我们对node的link更新node上的IP和ports,等待下次自动重连else:	/* node在我们这还不存在 */		/* 可以通过"cluster forget"命令把node从server的cluster节点字典中删掉,为了避免node的地址和端口被重用,错误连接到别的cluster,于是需要把删掉的node加入到server的黑名单中 */if sender存在 && gossip表示node是有地址的 &&gossip的nodename不在我们的黑名单中:创建node实体填入gossip的IP和ports加入cluster节点字典

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

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

相关文章

IPv4 、IPv6

以下是一个简单的表格&#xff0c;用于比较IPv4和IPv6的主要区别&#xff1a; 特性IPv4IPv6地址表示32位二进制数&#xff0c;点分十进制表示128位二进制数&#xff0c;冒号分隔的16进制表示地址长度32位&#xff08;约42亿个可能的地址&#xff09;128位&#xff08;几乎无限…

C语言中的异常处理机制是什么?

C语言中的异常处理机制 C语言是一门强大而灵活的编程语言&#xff0c;它为程序员提供了广泛的控制权和自由度。然而&#xff0c;C语言本身并不提供像其他高级语言一样的内置异常处理机制&#xff0c;如Java中的try-catch或Python中的异常处理。因此&#xff0c;C语言程序员需要…

中间件中使用到的设计模式

本文记录阅读源码的过程中&#xff0c;了解/学习到中间件使用到的设计模式及具体运用的组件/功能点 1. 策略模式 1. Nacos2.x中grpc处理时通过请求type来进行具体Handler映射&#xff0c;找到对应处理器。 2. 模板模式 1. Nacos配置数据读取&#xff0c;内部数据源、外部数据…

隐私交易成新刚需,Unijoin 凭什么优势杀出重围?

随着区块链技术的普及和发展&#xff0c;全球加密货币用户在持续增长&#xff0c;根据火币研究院公布的数据&#xff0c;2022年全球加密用户已达到 3.2亿人&#xff0c;目前全球人口总数超过了 80亿&#xff0c;加密货币用户渗透率已达到了 4%。 尤其是在 2020 年开启的 DeFi 牛…

如何像人类一样写HTML之图像标签,超链接标签与多媒体标签

文章目录 前言一、图像标签1.1 什么是图像标签&#xff1f;2.2 如何使用图像标签&#xff1f; 二、超链接标签2.1 什么是超链接标签&#xff1f;2.2 如何使用超链接标签&#xff1f; 三、多媒体标签3.1 什么是多媒体标签&#xff1f;3.2 如何使用多媒体audio标签&#xff1f;3.…

Python入门教程 | Python 常用标准库概览

Python3 标准库概览 Python 标准库非常庞大&#xff0c;所提供的组件涉及范围十分广泛&#xff0c;使用标准库我们可以让您轻松地完成各种任务。 以下是一些 Python3 标准库中的模块&#xff1a; os 模块&#xff1a;os 模块提供了许多与操作系统交互的函数&#xff0c;例如创…

【小沐学前端】Node.js实现基于Protobuf协议的UDP通信(UDP/TCP)

文章目录 1、简介1.1 node1.2 Protobuf 2、下载和安装2.1 node2.2 Protobuf2.2.1 安装2.2.2 工具 3、node 代码示例3.1 HTTP3.2 UDP单播3.4 UDP广播 4、Protobuf 代码示例4.1 例子: awesome.proto4.1.1 加载.proto文件方式4.1.2 加载.json文件方式4.1.3 加载.js文件方式 4.2 例…

多线程 - 单例模式

单例模式 ~~ 单例模式是常见的设计模式之一 什么是设计模式 你知道象棋,五子棋,围棋吗?如果,你想下好围棋,你就不得不了解一个东西,”棋谱”,设计模式好比围棋中的 “棋谱”. 在棋谱里面,大佬们,把一些常见的对局场景,都给推演出来了,照着棋谱来下棋,基本上棋力就不会差到哪…

TAADpapers的工具包用到的lru是lru-dict

TAADpapers的工具包用到的lru是lru-dict 运行时提示&#xff1a;no model named lru 安装lru时报错详情&#xff1a; (textattack-master) G:\xxx\TextAttack-master>pip install lru Collecting lruUsing cached lru-0.1.tar.gz (1.1 kB)Preparing metadata (setup.py) ..…

docker-compose一键启动neo4j

下载镜像 docker pull neo4j:3.5.22-community 编写配置文件 参考文档 编写docker-compose.yml文件 version: "3"services:neo4j:image: neo4j:3.5.22-communitycontainer_name: neo4j restart: alwaysports:- 7474:7474- 7687:7687environment:- NEO4J_AUTH:ne…

Scala第十五章节

Scala第十五章节 1. 递归 2. 案例一: 求阶乘 3. 案例二: 斐波那契数列 4. 案例三: 打印目录文件 scala总目录 文档资料下载

机器学习必修课 - 如何处理缺失数据

运行环境&#xff1a;Google Colab 处理缺失数据可简单分为两种方法&#xff1a;1. 删除具有缺失值的列 2. 填充 !git clone https://github.com/JeffereyWu/Housing-prices-data.git下载数据集 import pandas as pd from sklearn.model_selection import train_test_split导…

竞赛 机器视觉 opencv 深度学习 驾驶人脸疲劳检测系统 -python

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.2 打哈欠检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#x…

C语言实例_调用SQLITE数据库完成数据增删改查

一、SQLite介绍 SQLite是一种轻量级的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它是一个开源的、零配置的、服务器端的、自包含的、零管理的、事务性的SQL数据库引擎。它被广泛应用于嵌入式设备、移动设备和桌面应用程序等领域。 SQLite的特点包括&…

SpringBoot快速入门

搭建SpringBoot工程&#xff0c;定义hello方法&#xff0c;返回“Hello SpringBoot” ②导入springboot工程需要继承的父工程&#xff1b;以及web开发的起步依赖。 ③编写Controller ④引导类就是SpringBoot项目的一个入口。 写注解写main方法调用run方法 快速构建SpringBoo…

MySQL体系结构和四层架构介绍

MySQL体系结构图如下&#xff1a; 四层介绍 1. 连接层&#xff1a; 它的主要功能是处理客户端与MySQL服务器之间的连接(比如Java应用程序通过JDBC连接MySQL)。当客户端应用程序连接到MySQL服务器时&#xff0c;连接层对用户进行身份验证、建立安全连接并管理会话状态。它还处理…

python获取时间戳

使用 datetime 库获取时间。 获取当前时间&#xff1a; import datetime print(datetime.datetime.now()) . 后面的是微秒&#xff0c;也是一个时间单位&#xff0c;1秒1000000微秒。 转为时间戳&#xff1a; import datetimedate datetime.datetime.now() timestamp date…

小谈设计模式(14)—建造者模式

小谈设计模式&#xff08;14&#xff09;—建造者模式 专栏介绍专栏地址专栏介绍 建造者模式角色分类产品&#xff08;Product&#xff09;抽象建造者&#xff08;Builder&#xff09;具体建造者&#xff08;Concrete Builder&#xff09;指挥者&#xff08;Director&#xff0…

5G 技术、云原生开发和机器学习是推动物联网解决方案的重要助力

作者&#xff1a;Arm高级副总裁兼物联网事业部总经理 Paul Williamson 每次谈及物联网 (IoT)&#xff0c;行业就会明显出现两大阵营&#xff1a;乐观派和悲观派。后者将物联网认定为“尚属未来”的技术。而我个人是坚定的乐观派&#xff1a;对于物联网解决方案有望带来的变革性…

电脑通过串口助手和51单片机串口通讯

今天有时间把电脑和51单片机之间的串口通讯搞定了&#xff0c;电脑发送的串口数据&#xff0c;单片机能够正常接收并显示到oled屏幕上&#xff0c;特此记录一下&#xff0c;防止后面自己忘记了怎么搞得了。 先来两个图片看看结果吧&#xff01; 下面是串口3.c的文件全部内容&a…