redis不仅仅提供了sentinel模式的集群方案,还提供了一种称之为cluster模式的集群化方案,今天我们来学习一下redis的cluster集群,和sentinel集群方案不同,cluster是一个无中心化的集群方案,并且数据存储采用分片存储方式;接下来,让我们看看redis cluster部署图:
上图为redis cluster模式下的部署架构简图,各个redis实例之间建立两条连接,消息发送主动使用自己主动创建的连接和其他节点发送方消息;从上图可以看出来,在这个部署架构中,没有中心节点,在集群当中,各个节点的角色相同;
接下来我们逐步分析cluster集群工作的方式:
首先各个节点如何相互关联,一起构建出集群呢?
1、cluster集群需要在配置文件中修改配置项开启的,redis在默认情况下仅仅开启一个单例,不启动任何模式;redis 的cluster模式有一个单独的配置文件,在开启cluster模式后,redis会去读取cluster专属的配置文件,从配置文件中,解析出其他节点的相关信息,并保存在节点管理字典中;(需要注意的是,sentinel模式也是需要配置开启的,cluster模式和sentinel模式只能选择其中一种,不能同时开启)。
2、redis接受客户端或者终端将节点加入,构建集群,用户命令为"cluster meet ip port",ip和port是目标节点的地址,redis实例在收到请求命令后,会将请求中的地址解析出来,然后创建一个clusterNode节点,加入到节点管理字典中;
3、各个redis实例每个1s,都会随机选取一个符合条件的节点,向它发送一个ping消息,在消息中携带部分与本实例关联的其他redis实例的信息,在消息接收方,收到消息后,会解析携带的节点分消息,并跟新至自己维护的集群管理字典中;在处理完这些请求之后,消息接收方使用pong消息响应发送方,并且也选取自己维护的部分节点的信息,在pong消息中响应消息发送方。如此这样,就完成了节点信息的传播,这种传播方式称之为gossip方式(自行baidu/google了解);
通过上面的几种方式的结合使用,可以将一个一个的独立的redis实例,关联在一块,形成一个redis集群。
在分析clusetr进行集群维护之前,我们先来分一下clusterProcessPacket方法,该方法是redis clusetr集群信息交互的处理方式,该函数可以分成几部分进行分析:
上面截图代码是第一部分,也是共享部分;有与clusetr使用二进制通信协议,报文消息头是相固定的,但是针对于消息体部分,不同的消息类型,消息体是不同的;所以,部分是针对于消息进行解析分析的,通过上面这一部分,我们可以得出消息体的类型和消息
第二部分是上图中的代码;这部分代码主要针对于发送节点和接受节点是已经建立好连接的,由于发送节点在头部会携带自身的部分信息,所以在接受节点会根据消息的头部,更新发送节点在接受节点内存中维护的信息;
剩下的部分是针对于各种消息的具体处理逻辑,我们在接下来的逻辑处理部分详细讲解。
接下来,我们看看cluster模式下,各个节点是如何进行交互,保证redis集群的高可用的; 和redis中其他模块一样,cluster的主要触发工作也是在定时器当中,方法为clusterCron;可以将这个方法分为三大部分:a、创建到各个关联节点连接;b、站在本节点的角度,检查其他关联节点的状态;c、判断是否存在故障节点,并进行故障转移;接下来,我们从分别从以上几点进行讲解分析;(大家需要注意一下,每个redis节点在本身秋有一个全局变量用以标注自身,该全局变量名称为“myself”,后面我们经常会用到这个变量)
创建连接的代码为进入clusterCron方法后的第一个while循环;代码如上,循环中的每一个node对象,即为本节点目前知道的,存在集群中的一个实例;针对于当前节点本身或者目前还不知道地址的节点,直接跳过;进入下一个节点判断,针对于已经超时且正处于握手状态的节点,有可能是节点已经不存在了,直接从节点管理字典中删除;接下来,针对于还没有创建到本节点连接的节点,先进行节点连接的创建(节点的连接是基于tcp的长连接),然后设置通信回调处理方法(clusterReadHandler),接着向节点发送一个meet消息(clusterSendPing);到此,本节点与对端节点的连接已经建立;
对端节点的在收到本节点发送的meet消息后如何处理呢?
由于cluster的消息处理都在clusterProcessPacket方法中,接下来我们看一下meet消息的处理逻辑:
由于这个是发送节点(sender)和本节点的第一个通信消息,如果本节点刚刚启动,则通过通信的fd获取本节点的ip信息(这样做的目的是为了防止配置文件中配置的ip地址被修改过,有可能是重新分配的网段,也有可能配置文件被破坏过,使用这种方式获取到的地址信息是准确可信的);
如果sender节点是本节点不知道的节点(说明其中的一个节点是刚刚加入到集群中的),则会创建一个sender节点的实例对象,保存到本节点的集群节点管理字典中(本节点的下一个时间周期到达后,会触发本节点向sender节点发起连接,这样双向连接就能够建立);并且本节点不知道sender节点,则会解析消息体;处理完上面的步骤后,会响应一个pong消息给发送方;
如果sender节点是已知节点,且sender节点的报文中没有主节点的名称信息为空,则说明sender节点自己为一个主节点,这样的话,本节点会在标注sender节点为主节点;如果sender节点的消息中报告自身为slave节点,而在本节点维护的节点消息中,sender有为master节点,则需要修改本节点维护的sender节点的消息,修改sender节点的标识位,并且修改从节点的槽位;如果sender消息中携带的自身维护的槽位与本节点维护的sender节点对象中维护的槽位信息不同,则需要修改槽位信息;最后解析消息中携带的消息体;这部分处理流程,ping、pong、meet消息共享
b、站在本节点角度检查其他关联节点的状态
定时器每隔1s,会随机的选取5个节点,在者五个节点中,选取响应距离当前时间最长的的一个节点发送ping消息,这样是为了检测节点是否的存活,同时可以更节点信息;
接下来,循环遍历节点管理字典中的所有节点,针对于没有发送过ping消息、正处于握手状态、没有地址状态以及myself状态的节点,直接跳过,不做检测;如果本节点是slave节点,统计没有下线的master节点中,slave节点最多的个数,和没有slave的masetr节点的数量;针对于长时间没有响应消息的节点,,将其节点对象释放;针对于在进行故障转移的从节点或者长时间没有通信但是没有标注为故障的节点,向他们发送ping消息;最后,计算节点到目前为止,已经发送了ping消息,但是没有收到响应的时间。如果时间间隔已经超过离线集群时间阈值,者标记为疑似下线状态。到目前为止,clusetr已经完成了对节点的检测。
涉及到的节点转移的内容比较多,后面专门分享一篇