Redis 源码剖析与实战
蒋德钧
中科院计算所副研究员
17747 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
Redis 源码剖析与实战
15
15
1.0x
00:00/00:00
登录|注册

26 | 从Ping-Pong消息学习Gossip协议的实现

你好,我是蒋德钧。
从这节课开始,我们又将进入一个新的模块:“Redis Cluster”模块。在这个模块中,我会带你了解 Redis Cluster 的关键功能实现,包括了 Gossip 协议通信、集群关键命令和数据迁移等机制的设计与实现。
通过这些课程的学习,一方面,你可以深入了解 Redis 是如何完成集群关系维护、请求转发和数据迁移的。当你遇到集群问题时,这些知识可以帮助你排查问题。另一方面,当你在开发分布式集群时,不可避免地会遇到节点信息维护、数据放置和迁移等设计问题,接下来的几节课可以让你掌握 Gossip 协议、数据迁移等分布式集群中关键机制的典型设计和实现,而这些实现方法对于你开发分布式集群是很有帮助的。
那么接下来,我就先带你来学习 Redis Cluster 中节点的通信机制,而这个通信机制的关键是 Gossip 协议。所以今天这节课,我们主要来了解下 Gossip 协议在 Redis 中是如何实现的。

Gossip 协议的基本工作机制

对于一个分布式集群来说,它的良好运行离不开集群节点信息和节点状态的正常维护。为了实现这一目标,通常我们可以选择中心化的方法,使用一个第三方系统,比如 Zookeeper 或 etcd,来维护集群节点的信息、状态等。同时,我们也可以选择去中心化的方法,让每个节点都维护彼此的信息、状态,并且使用集群通信协议 Gossip 在节点间传播更新的信息,从而实现每个节点都能拥有一致的信息。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Redis Cluster使用Gossip协议实现节点间通信,确保集群节点信息和状态的一致性。Gossip协议通过随机选择通信节点,以PING和PONG消息传播节点信息和状态,实现所有节点维护一致状态。Redis的Gossip协议实现涉及节点间通信消息的定义和数据结构,以及消息的具体收发实现。通信消息类型包括Ping、Pong、Meet和Fail等,消息的数据结构clusterMsg包含发送节点信息、消息类型、长度和消息体。消息体clusterMsgData定义了不同类型消息的具体数据结构,如clusterMsgDataGossip用于表示节点间通信的基本信息和运行状态。了解Redis中Gossip协议的实现方法,对开发分布式集群的读者提供有益的参考和帮助。 文章详细介绍了clusterCron函数的执行逻辑,以及clusterSendPing函数中构建和发送Ping消息的过程,包括构建Ping消息头、构建Ping消息体和发送消息。文章还介绍了节点收到Ping消息后的处理,即Pong消息的发送。通过对这些内容的了解,读者可以深入理解Redis Cluster中Gossip协议的实现细节,为他们开发分布式集群提供指导和帮助。 Gossip协议的关键在于通过Ping-Pong消息传播节点信息,以及随机选择节点发送消息。Redis Cluster设计了clusterMsg结构的消息,其中消息头包含了发送消息节点自身的信息,而消息体使用了clusterMsgDataGossip类型的数组,传播节点已知的其他节点的信息。随机选择节点发送消息的实现在源码中比较容易,通过clusterCron函数随机选择节点并挑选最长时间未发送Pong消息的节点作为目标节点,满足了Gossip协议的要求。 通过本文,读者能了解Redis Cluster设计的消息结构、周期发送Ping和Pong消息的整体执行逻辑,为开发Gossip协议提供了经典参考设计。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Redis 源码剖析与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(6)

  • 最新
  • 精选
  • Kaito
    1、多个节点组成一个分布式系统,它们之间需要交换数据,可以采用中心化的方式(依赖第三方系统,例如ZK),也可以采用非中心化(分布式协议,例如 Gossip)的方式 2、Redis Cluster 采用非中心化的方式 Gossip 协议,实现多个节点之间信息交换 3、集群中的每个实例,会按照固定频率,从集群中「随机」挑选部分实例,发送 PING 消息(自身实例状态、已知部分实例信息、slots 分布),用来交换彼此状态信息 4、收到 PING 的实例,会响应 PONG 消息,PONG 消息和 PING 消息格式一样,包含了自身实例状态、已知部分实例信息、slots 分布 5、这样经过几次交换后,集群中每个实例都能拿到其它实例的状态信息 6、即使有节点状态发生变化(新实例加入、节点故障、数据迁移),也可以通过 Gossip 协议的 PING-PONG 消息完成整个集群状态在每个实例上的同步 课后题:为什么 clusterSendPing 函数计算 wanted 值时,是用的集群节点个数的十分之一? 这个和 Redis Cluster 判定实例「故障」逻辑有关了。 Redis Cluster 实例在周期性向其它实例交换信息时,会先随机选出 5 个实例,然后从中找出最久没通信过的实例,发送 PING 消息。 但这里有个问题,随机选出的这 5 个实例,有可能并不是整个「集群」中最久没通信过的,为了避免拿不到这些实例的状态,导致集群误以为这些实例已过期,所以制定了一个策略:如果和实例最近通信时间超过了 cluster-node-timeout / 2,那会立即向这个实例发送 PING 消息。 每次 PING 都会收到 PONG 响应,一来一回 2 次心跳包,来回都带有部分实例的状态信息,那在 cluster-node-timeout 时间内会收到 4 次心跳包。 又因为 Redis Cluster 计算故障转移超时时间是 cluster-node-timeout * 2,那这段时间内就能收到 8 个 PING + PONG 心跳包,每个心跳包中实例个数设置为集群的 1/10,那在故障转移期间就能收到集群 80%(8 * 1/10)节点发来的故障状态信息了,满足集群大部分节点发来的节点故障情况。
    2021-10-09
    2
    21
  • 🙄
    关于PING消息 用的集群节点个数的十分之一作为wanted值如课代表所说,但是在我阅读源码的时候发现,PING消息是先把wanted数量的实例放到消息体,然后再把所有当前nodes认为是PFAIL的实例放到消息体末尾,也就是说,新版的redis实例加速了 PFAIL->FAIL的判断,跟十分之一的关系已经不大了。 PS: 当时看这段代码的时候非常疑惑,看源码的注释也很疑惑...直到翻阅github上面的提交记录 https://github.com/redis/redis/commit/1e659a04cf19e4349c8dbba931d1606336970b8c#diff-55c2de0fa49d05f6ed8f0c13cacedc85fba5d5739c8360567743f9f740df3179 /* If there are PFAIL nodes, add them at the end. */ if (pfail_wanted) { dictIterator *di; dictEntry *de; di = dictGetSafeIterator(server.cluster->nodes); while((de = dictNext(di)) != NULL && pfail_wanted > 0) { clusterNode *node = dictGetVal(de); if (node->flags & CLUSTER_NODE_HANDSHAKE) continue; if (node->flags & CLUSTER_NODE_NOADDR) continue; if (!(node->flags & CLUSTER_NODE_PFAIL)) continue; clusterSetGossipEntry(hdr,gossipcount,node); freshnodes--; gossipcount++; /* We take the count of the slots we allocated, since the * PFAIL stats may not match perfectly with the current number * of PFAIL nodes. */ pfail_wanted--; } dictReleaseIterator(di); }
    2021-10-15
    2
  • 边际革命
    主从如何做故障切换的 不讲一下吗?
    2022-04-26
  • neohope
    为何选择了1/10的节点,在源码注释里其实就有,分两部分解释一下: 一、在clusterCron函数中,有一个强制PING策略: 如果节点A和节点B最后通信时间超过了 cluster-node-timeout/2,节点A会立即向节点B发送 PING 消息。所以,在在cluster-node-timeout时间内,节点A和节点B最少也会收发2来2回共4次心跳包。 Redis Cluster计算故障转移确认时间是cluster-node-timeout*2,那这段时间内节点A和节点B最少会收发4来4回共8个心跳包。 二、在一个100个全master节点的集群中,有一个正常节点A,一个被A判断为PFAIL节点C,在没有pfail_wanted的时候: 对于节点A,在cluster-node-timeout*2的故障转移确认时间内,最少也可以与他节点交换关于C节点到这么多个包: PROB * GOSSIP_ENTRIES_PER_PACKET * TOTAL_PACKETS (节点C被包含在一个entry中的几率,100选1,也就是1%)*(一个GOSSIP包中的entry数量,10分之1,100个节点网络中是10)*(其他节点与A交换的最小总包数,节点数*8) 1%*10*(100*8)=80 由于这些包都是随机选择entry的,节点A收发的这80个包含C节点信息的包,也就是与A交换过C节点信息的节点也差不多为80个,大概率覆盖了100个节点的多数节点,也就可以确实证明了C点有问题了。 为何不再高一些:现在这个比例已经够高了,如果再高一些,只会增加网络负担而已。而且,收发8个包已经是最差情况了,平时比8个包还会多一些。 同时,在4.x的源码中,已经补充了pfail_wanted的代码,会让PFAIL节点更快的传播。 为何不再低一些:在正常的redis cluster集群中,有一些slave节点,不会参与投票,所以保持了这样一个比例。 此外,这里就是个估算,别纠结为何用100不用99或98什么的,不影响结论。也不用纠结这80个包,如果收发全部重叠,不就只有40个节点交换信息吗,估算时不要考虑小概率时间。
    2022-03-25
  • OAuth
    这个达到最终一致性是Gossip中的谣言传播吗
    2022-02-16
  • 李艳伟
    老师我想问一下,我遇到一个问题,就是一个正常集群由于节点故障产生了一条fail信息,没有及时清理,这个ip又被别的集群使用了,请问这个是ping pong信息交换的,还是meet呢
    2021-12-18
收起评论
显示
设置
留言
6
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部