集群
FAIL消息的实现
当集群里的主节点A将主节点B标记为已下线(FAIL)时,主节点A将向集群广播一条关于主节点B的FAIL消息,所有接收到这条FAIL消息的节点都会将主节点B标记为已下线。在集群的节点数量比较大的情况下,单纯使用Gossip协议来传播节点的已下线信息会给节点的信息更新带来一定延迟,因为Gossip协议消息通常许需要一段时间才能传播至整个集群,而发送FAIL消息可以让集群里的所有节点立即知道某个主节点已下线,从而尽快判断是否需要将集群标记为下线,又或者对下线主节点进行故障转移。FAIL消息的正文由cluster.h/clusterMsgDataFail结构表示,这个结构只包含一个nodename属性,该属性记录了已下线节点的名字:
typedef struct {
char nodename[REDIS_CLUSTER_NAMELEN];
}clusterMsgDataFail;
因为集群里的所有节点都有一个独一无二的名字,所以FAIL消息里面只需要保存下线节点的名字,接收到消息的节点就可以根据这个名字来判断是哪个节点下线了。
例子
- 举个例子。对于包含7000、7001、7002、7003四个主节点的集群来说:
1.如果主节点7001发现主节点7000已下线,那么主节点7001将向主节点7002和主节点7003发送FAIL消息,其中FAIL消息中包含的节点名字为主节点7000的名字,以此来表示主节点7000已下线
2.当主节点7002和主节点7003都接收到主节点7001发送的FAIL消息时,它们也会将主节点7000标记为已下线
3.因为这时集群已经有超过一半的主节点认为主节点7000已下线,所以集群剩下的几个主节点可以判断是否需要将集群标记为下线,又或者开始对主节点7000进行故障转移
图中展示了节点发送和接收FAIL消息的整个过程
PUBLISH消息的实现
当客户端向集群中的某个节点发送命令:
PUBLISH <channel> <message>
的时候,接收到PUBLISH命令的节点不仅会向channel频道发送消息message,他还会向集群广播一条PUBLISH消息,所有接收到这条PUBLISH消息的节点都会向channel频道发送message消息。换句话说,向集群中的某个节点发送命令:
PUBLISH <channel> <message>
PUBLISH消息的正文由cluster.h/clusterMsgDataPublish结构表示:
typedef struct {
uint32_t channel_len;
uint32_t message_len;
// 定义为8字节只是为了对齐其他消息结构
// 实际的长度由保存的内容决定
unsigned char bulk_data[8];
}clusterMsgDataPublish;
clusterMsgDataPublish结构的bulk_data属性是一个字节数组,这个字节数组保存了客户端通过PUBLISH命令发送给节点的channel参数和message参数,而结构的channel_len和message_len则分别保存channel参数的长度和message参数的长度:
- 1.其中bulk_data的0字节至channel_len -1字节保存的是channel参数
- 2.而bulk_data的channel_len字节至channel_len + message_len - 1字节保存的则是message参数
例子
- 举个例子。对于包含7000、7001、7002、7003四个节点的集群来说,如果节点7000收到了客户端发送的PUBLISH命令,那么节点7000将向7001、7002、7003三个节点发送PUBLISH消息,如图所示
- 举个例子。如果节点收到的PUBLISH命令为:
PUBLISH "news.it" "hello"
那么节点发送的PUBLISH消息的clusterMsgDataPublish结构将如图所示:
其中bulk_data数组的前七个字节保存了channel参数的值"news.it"而bulk_data数组的后五个字节则保存了message参数的值"hello"
为什么不直接向节点广播PUBLISH命令?
实际上,要让集群的所有节点都执行相同的PUBLISH命令,最简单的方法就是向所有节点广播相同的PUBLISH命令,这也是Redis在复制PUBLISH命令时所使用的方法,不过因为这种做法并不符合Redis集群的"各个节点通过发送和接收消息来进行通信"这一规则,所以节点没有采取广播PUBLISH命令的做法