Redis(三十五):集群,你有过迷茫吗

uint64_t currentEpoch;

//这个数据也是记录配置纪元,只不过有些不同

//如果这个是从节点,记录的是正在复制主节点的配置纪元

//如果这个是主节点,记录的就是自己的配置纪元

uint64_t configEpoch;

//发送者的名字(ID)

char sender[REDIS_CLUSTER_NAMELEN];

//发送者目前的槽指派信息

unsigned char myslots[REDIS_CLUSTER_SLOTS/8];

//记录的是主节点名字

//如果这个是从节点,那么这里记录的是正在复制的主节点的名字

//如果这个是主节点,那么这里记录的是REDIS_NODE_NULL_NAME

//REDIS_NODE_NULL_NAME是一个40字节长,但值全为0的字节数组

char slaveof[REDIS_CLUSTER_NAMELEN];

//发送者的端口号

uint16_t port;

//发送者的标识值(主还是从,还是正在处于MEET等)

unsigned flags;

//发送者所处集群的状态(上线还是下线)

unsigned char state;

//消息的正文

union clusterMsgData data;

)clusterMsg;

可以看到使用的是data属性保存消息的正文,指向的是一个clusterMsgData结构(这个结构保存着所有5种消息,而每种消息都由对应的结构去储存)

typedef clusterMsgData(

//MEET、PING、PONG消息的正文

struct(

//每条MEET、PING、PONG消息都包含两个

//clusterMsgDataGossip结构

clusterMsgDataGossip gossip[1];

)

//FAIL消息的正文

struct(

clusterMsgDataFail about;

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

开源分享完整内容戳这里

)fail

//PUBLISH消息的正文

struct(

clusterMsgDataPublish msg;

)

//…其他消息的正文

)

可以看到,在消息头里面,有着一系列发送者的信息,那么接收者就可以根据这些信息去对应找到clusterState.nodes里面的clusterNode结构,并且对应去更新里面的信息,比如修改里面的槽指派信息,还有flag标识

MEET、PING、PONG消息的实现

Redis集群中的各个节点通过Gossip协议来交换各自关于不同节点的状态信息,其中Gossip协议由MEET、PING、PONG三种信息来实现,这三种消息的正文都由clusterMsgDataGossip结构组成的

union clusterMsgData(

//…

//MEET、PING、PONG消息的正文

struct(

//每条MEET、PING、PONG消息都包含两个clusterMsgDataGossip结构

clusterMsgDataGossip gossip[1];

)ping;

//其他消息的正文

)

这里可能就在想,那么如何区分MEET、PING、PONG消息呢?

这个是通过消息头里面的type属性来判断一条消息是MEET消息、PING消息还是PONG消息

每次发送MEET、PONG、PING消息时,发送者都从自己的已知节点列表中随机选出两个节点(可以是主节点也可以是从节点),并将这两个被选中节点的信息分别保存到clusterMsgDataGossip结构里面,所以每条MEET、PONG、PING消息都包含两个clusterMsgDataGossip结构

clusterMsgDataGossip结构记录了被选中节点的名字,发送者与被选中节点最后一次发送和接收PING消息和PONG消息的时间戳,被选中节点的IP地址和端口号,以及被选中节点的标识值

typedef struct clusterMsgDataGossip(

//被选中节点的名字

char nodename[REDIS_CLUSTER_NAMELEN];

//发送者最后一次向该节点发送PING消息的时间戳

uint32_t ping_sent;

//发送者最后一次从该节点接收PONG消息的时间戳

uint32_t pong_received;

//节点的IP地址

char ip[16];

//节点的端口号

uint16_t port;

//节点的标识值

uint16_t flags;

)clusterMsgDataGossip;

当接收者收到MEET、PING、PONG消息时,接收者会访问消息正文里面的两个clusterMsgDataGossip结构,并根据,然后对应去搜索自己的clusterState.node属性,看自己是否认识clusterMsgDataGossip里面的被选中的节点,然后对应进行下列的操作

  • 如果被选中的节点不存在于接受者的已知节点链表里面,那么就代表接收者是第一次接触到被选中的节点,那么就要进行握手操作,接收者要根据结构中记录的IP地址和端口号等信息,与被选中的节点进行握手

  • 如果被选中的节点存在于接收者的已知节点链表里面,那么说明接收者已经接触过被选中的节点,那么就要进行更新操作,接收者要根据结构中记录的信息,对被选中的节点进行一个状态更新,及更新对应的clusterNode结构

  • 但对于MEET消息,是不是也应该要跟发送方进行握手呢?

FAIL消息的实现

当集群里的主节点A将主节点B标记为下线时,会通过集群广播一条关于主节点B的FAIL消息,所有接收到这条消息的节点(包括主从节点)都会将主节点B标记为下线

FAIL消息不使用Gossip协议去实现,因为Gossip协议在节点数量比较多的集群里面会有延迟(因为每次只告诉一个节点,而且只告诉发送者认知的两个节点的信息),但节点已下线消息需要实时性比较强,所以使用FAIL消息进行发送

FAIL消息是使用clusterMsgDataFail结构表示,而这个结构只包含一个nodename属性,用来记录已下线节点的名字

typedef struct clusterMsgDataFail(

char nodename[REDIS_CLUSTER_NAMELEN];

)clusterMsgDataFail

因为集群里的所有节点都有一个独一无二的名字(回看前面的文章,从节点的名字是ip地址加端口号),所以FAIL消息里面只需要保存下线节点的名字即可,接收到FAIL消息的节点就可以根据这个名字去判断哪个节点下线了,然后进行对应的下线操作(将节点标记为已下线)。

PUBLISH消息的实现

当客户端向集群中的某个主节点发送命令:

PUBLISH

那么接收到PUBLISH命令的主节点不仅会执行这条命令(即发送消息到对应的频道),还会通过集群广播一条PUBLISH消息,所有接收到这条PUBLISH消息的节点都会执行命令(即发送消息到对应的频道)

也就是说,上面的那条命令操控的不是一个主节点,而是整个主节点所在的集群,集群中的所有节点都会向指定的频道发送消息。

PUBLISH消息的正文由clusterMsgDataPublish正文表示

typedef struct(

//channel参数的长度(字节,通常一个英文或符号代表1字节)

uint32_t channel_len;

//msg参数的长度(字节,通常一个因为或符号代表1字节)

uint32_t message_len;

//保存的命令

//这里并不一定是8字符长度,按给的命令来计算的

//bulk:主体

上一篇:python_3_字符串


下一篇:virtualBox + centos7 设置host-only模式静态ip