Redis(源码剖析):50---集群之集群数据结构(clusterNode、clusterLink、clusterState、CLUSTER MEET命令的实现)

一、struct clusterNode

  • clusterNode结构保存了一个节点的当前状态,比如节点的创建时间、节点的名字、节点 当前的配置纪元、节点的IP地址和端口号等等
  • 每个节点都会使用一个clusterNode结构来记录自己的状态,并为集群中的所有其他节点 (包括主节点和从节点)都创建一个相应的clusterNode结构,以此来记录其他节点的状态:
struct clusterNode {
    //创建节点的时间
    mstime_t ctime;

    //节点的名字,由40 个十六进制字符组成
    //例如68eef66df23420a5862208ef5b1a7005b806f2ff
    char name[REDIS_CLUSTER_NAMELEN];

    //节点标识
    //使用各种不同的标识值记录节点的角色(比如主节点或者从节点),
    //以及节点目前所处的状态(比如在线或者下线)。
    int flags;

    //节点当前的配置纪元,用于实现故障转移
    uint64_t configEpoch;

    //节点的IP 地址
    char ip[REDIS_IP_STR_LEN];

    //节点的端口号
    int port;

    //保存连接节点所需的有关信息
    clusterLink *link;
    // ...
};

二、struct clusterLink

  • clusterNode结构的link属性是一个clusterLink结构,该结构保存了连接节点所需的有关信息,比如套接字描述符,输入缓冲区和输出缓冲区:
typedef struct clusterLink {
    //连接的创建时间
    mstime_t ctime;

    // TCP 套接字描述符
    int fd;

    //输出缓冲区,保存着等待发送给其他节点的消息(message )。
    sds sndbuf;

    //输入缓冲区,保存着从其他节点接收到的消息。
    sds rcvbuf;

    //与这个连接相关联的节点,如果没有的话就为NULL
    struct clusterNode *node;
} clusterLink;
  • redisClient结构和clusterLink结构的相同和不同之处:
    • redisClient结构和clusterLink结构都有自己的套接字描述符和输入、输出缓冲区,这两个结构的区别在于,redisClient结构中的套接字和缓冲区是用于连接客户端的,而clusterLink结构中的套接字和缓冲区则是用于连接节点的

三、struct clusterState

  • 最后,每个节点都保存着一个clusterState结构,这个结构记录了在当前节点的视角下, 集群目前所处的状态,例如集群是在线还是下线,集群包含多少个节点,集群当前的配置纪元,诸如此类:
typedef struct clusterState {
    //指向当前节点的指针
    clusterNode *myself;

    //集群当前的配置纪元,用于实现故障转移
    uint64_t currentEpoch;

    //集群当前的状态:是在线还是下线
    int state;

    //集群中至少处理着一个槽的节点的数量
    int size;

    //集群节点名单(包括myself 节点)
    //字典的键为节点的名字,字典的值为节点对应的clusterNode 结构
    dict *nodes;
    // ...
} clusterState;

演示案例

  • 以前一篇文章中介绍的7000、7001、7002三个节点为例,下图展示了节点7000创建的clusterState结构,这个结构从节点7000的角度记录了集群以及集群包含的三个节点的当前状态(为了空间考虑,图中省略了clusterNode结构的一部分属性):
    • 结构的currentEpoch属性的值为0,表示集群当前的配置纪元为0
    • 结构的size属性的值为0,表示集群目前没有任何节点在处理槽,因此结构的state属性的 值为REDIS_CLUSTER_FAIL,这表示集群目前处于下线状态
    • 结构的nodes字典记录了集群目前包含的三个节点,这三个节点分别由三个clusterNode 结构表示,其中my self指针指向代表节点7000的clusterNode结构,而字典中的另外两个指针 则分别指向代表节点7001和代表节点7002的clusterNode结构,这两个节点是节点7000已知的 在集群中的其他节点

    • 三个节点的clusterNode结构的flags属性都是REDIS_NODE_MASTER,说明三个节点都是主节点

  • 节点7001和节点7002也会创建类似的clusterState结构:
    • 不过在节点7001创建的clusterState结构中,my self指针将指向代表节点7001的 clusterNode结构,而节点7000和节点7002则是集群中的其他节点
    • 而在节点7002创建的clusterState结构中,my self指针将指向代表节点7002的clusterNode 结构,而节点7000和节点7001则是集群中的其他节点

三、CLUSTER MEET命令的实现

  • 通过向节点A发送CLUSTER MEET命令,客户端可以让接收命令的节点A将另一个节点B添加到节点A当前所在的集群里面:
CLUSTER MEET <ip> <port>
  • 收到命令的节点A将与节点B进行握手(handshake),以此来确认彼此的存在,并为将来的进一步通信打好基础:
    • ①节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的 clusterState.nodes字典里面
    • ②之后,节点A将根据CLUSTER MEET命令给定的IP地址和端口号,向节点B发送一条 MEET消息(message)
    • ③如果一切顺利,节点B将接收到节点A发送的MEET消息,节点B会为节点A创建一个 clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面
    • ④之后,节点B将向节点A返回一条PONG消息
    • ⑤如果一切顺利,节点A将接收到节点B返回的PONG消息,通过这条PONG消息节点A 可以知道节点B已经成功地接收到了自己发送的MEET消息
    • ⑥之后,节点A将向节点B返回一条PING消息
    • ⑦如果一切顺利,节点B将接收到节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功地接收到了自己返回的PONG消息,握手完成

图示

  • 下图展示了以上步骤描述的握手过程

  • 之后,节点A会将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也 与节点B进行握手,最终,经过一段时间之后,节点B会被集群中的所有节点认识
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值