《Redis设计与实现》读书笔记(三十一) ——Redis集群消息类型
(原创内容,转载请注明来源,谢谢)
1、发送消息类型
集群中节点通过发送与接收消息进行通信。
发送消息的节点称为消息发送者,接收消息节点称为接收者。消息发送类型如下:
1)meet
当客户端发送clustermeet给节点,节点会发送meet消息给接收者,请求接收者加入到发送者当前的集群中。
2)ping
每个节点每秒,默认会随机从当前已知节点列表,挑选5个节点,并从中挑选最久未发送过ping消息的节点,对其发送ping,检测其是否在线。
另外,如果某个节点最后一次回复pong的时间,距离当前时间,已经超过redis配置文件中cluster-node-timeout设定的秒数的一半,则也会对该节点发送ping,防止多次没有随机到该节点,导致对该节点的状态更新过慢。
3)pong
当节点收到meet或者ping,为了告知发送者收到消息,会回复pong。
另外,完成一次故障转移后,新的主节点会给向集群广播pong。
4)fail
当节点认为某个节点下线,会向集群广播关于该节点的fail状态,其他节点接收到后,都会将该节点状态置为下线。
5)publish
当节点收到publish命令,会执行该命令,并向集群发送publish,其他节点收到后也会执行该命令。
2、消息头
所有消息都由消息头包裹,消息头可以认为是消息的一部分。消息头由cluster.h/clusterMsg结构记录,如下:
structclusterMsg{
uint32_t totlen;//消息总长度,包括消息头长度和正文长度
uint16_t type;//消息类型
uint16_t count;//消息正文包含节点信息数量,只有在meet、ping、pong这三种涉及到gossip协议的类型使用
uint64_t currentEpoch;//发送者的配置纪元
uint64_t configEpoch;//该节点是主节点时,是发送者的配置纪元;是从节点时,是对应正在复制的主节点的配置纪元
char sender[REDIS_CLUSTER_NAMELEN];//发送者名字(ID)
unsigned char myslots[REDIS_CLUSTER_SLOTS/8];//发送者目前的槽信息
char slaveof[REDIS_CLUSTER_NAMELEN];//主节点时记录的是40位长的都是0的字符串,从节点时记录的是复制的主节点的名字
uint16_t port;//发送者端口号
uint16_t flag;//发送者标识值
unsigned char state;//发送者所处的集群状态
union clusterMsgData data;//消息的正文
}clusterMsg;
消息的正文是一个联合体,共有三种类型结构体,包括ping、fail、publish,其中pong、meet类型都和ping一样。
3、meet、ping、pong
这三个的类型一样,都是记录在联合体clusterMsgData中的结构体。因为这三种消息有相同的正文,节点是通过消息头的type判断是这三种的哪一种。
这三种消息的类型是clusterMsgDataPublish,这是一个结构体,记录节点名字、最后给该节点发送ping的时间戳、最后收到节点pong的时间戳、节点ip、端口号、标识等。
当节点接收到信息时,如果不认识里面的节点,则会与节点进行握手,如果认识则更新对应的信息。
4、fail
由于使用gossip协议会有延迟,fail是用来表示该节点下线,需要尽快传达,因此不用gossip协议,而是立即让集群中的全部节点知情。其正文就是下线节点的名字。
5、publish
客户端向集群发送publish<channel> <message>,接收的频道不仅会向频道channel发送message,还会向集群广播publish,其他节点也会执行该命令。
因此,向某个节点发送publish,会导致所有节点都执行该命令。
具体流程如下:
publish用结构体clusterMsgDataPublish记录,内容是包括频道长度,消息长度,以及具体内容。
其中,bulk_data的前channel_len字节,记录channel参数;剩余字节记录message参数。
例如,发送publish“news.it” “hello” 如下:
其实也可以直接向集群广播publish命令,但是由于其不符合redis设计的各节点通过消息发送和接收来传播消息的做法,因此采用对某一节点进行消息发送。
——written by linhxx 2017.09.18