一个Redis集群通常是由多个节点组成的(一个节点对应的就是一台服务器),在刚开始还没有集群的时候,一个节点都是独立的,都分别处于一个只包含自己的集群中,要组建一个高可用的集群,需要将各个独立的节点连接起来,构成一个拥有多节点的集群里面。
cluster nodes //查看当前节点集群的信息
cluster meet ip地址 端口号 //让指定ip地址和端口号的服务器加入到自己的集群
使用cluster meet 时,可以当前节点与指定节点进行握手,当握手成功时,当前节点就会将返回握手响应的节点加入到当前集群中去
一个节点其实就是一个运行在集群模式下的Redis服务器,判定是集群模式还是单机模式(普通Redis服务器)是根据配置文件的cluster-enabled配置选项是否为Yes来判定的
所以节点其实就是运行在集群模式下的Redis服务器,虽然在集群模式下,但其实其功能跟单机模式下的Redis差不多,因为他会继续所用所有在单机模式中使用的服务器组件
-
节点会继续使用文件事件处理器来处理命令请求和返回命令回复
-
节点也会继续使用时间事件处理器去执行ServerCron函数,而ServerCron函数里面的内容发生了变化,与普通的Redis服务器不一样,他会专门去执行ClusterCron函数(ClusterCron函数包含了集群模式下需要执行的常规操作)。
-
节点继续使用键空间保存键值对对象,键值对对象的类型仍然是前面提到的5种
-
节点继续使用AOF和RDB持久化模块进行持久化
-
节点继续使用发布与订阅模块
-
节点继续使用复制模块(节点之间也有主从关系)
-
节点会继续使用Lua脚本环境来执行客户端输入的Lua脚本
可以看到,普通Redis服务器有的功能,节点也会有
除此之外,像Sentinel一样,节点服务器也有属于节点状态结构(Sentinel有一个SentinelStat结构),ClusterNode、ClusterLink结构以及ClusterState结构里面。
当然,服务器状态还是保存在RedisServer结构中
clusterNode结构保存了一个节点的当前状态,比如节点的创建时间、节点的名字、节点当前的配置纪元(集群也有主从节点,也要进行故障转移)、节点的IP地址和端口号等等
当使用集群模式开启了服务器时,就代表这个服务器是一个节点,节点会用clusterNode结构来记录自己的状态,并为集群中的所有节点(主节点和从节点)都会去创建一个相应的clusterNode结构,用该结构去记录其他节点的状态
struct clusterNode(
//创建节点时间
mstime_t ctime;
//节点的名字
//由40个十六进制字符组成
char name[REDIS_CLUSTER_NAMELEN];
//节点标识
//用来区分节点是主还是从节点
//以及区分节点的状态(上线还是下线)
int flags;
//节点当前的配置纪元,用于实现故障转移
unit64_t configEpoch;
//节点的ip地址
char ip[REDIS_IP_STR_LEN];
//节点的端口号
int port;
//保存连接节点所需的有关信息(TCP建链连接)
clusterLink *link;
//…
)
前面提到过,ClusterNode结构之外,还有一个ClusterLink结构,从ClusterNode结构里面的属性看到,ClusterLink结构是被用在ClusterNode里面的,并用来保存连接节点所需的有关信息,比如套接字描述符,输入缓冲区和输出缓冲区
typedef struct clusterLink(
//连接的创建时间
mstime_t ctime;
//TCP套接字描述符(记录节点连接当前节点使用的套接字)
int fd;
//输出缓冲区(保存着等待发送给其他节点的消息)
sds sndbuf
//输入缓冲区(保存着从其他节点接收到的信息)
sds rcvbuf;
//与这个连接相关联的节点,如果没有的话就为NULL(也就是引用clusterLink的节点)
//相当于形成了一个互通
struct clusterNode *node;
)clusterLink
拓展
RedisClient结构与ClusterLink结构的相同与不同之处
相同之处在于,两者都有记录自己的套接字描述符和输入输出缓冲区
不同之处在于,RedisClient的套接
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
字和缓冲区是用来连接客户端的(服务器根据这些去得到请求客户端的信息,通过输入缓冲区和输出缓冲区进行命令提取和回复),而ClusterLink是用来连接节点的(当前节点通过这个节点的ClusterNode里面的ClusterLink里面的信息进行连接,获取发来的消息或者发送回复)
最后,每一个节点都保存着一个ClusterState结构(上面提到的ClusterNode都是保存在ClusterState俩面的),该结构记录了在当前节点的视角下,集群目前所处的状态,例如集群是下线的还是上线,集群包含多少个节点,集群当前的配置纪元
typedef struct clusterState(
//指向当前节点的指针
clusterNode *myself;
//集群当前的配置纪元,用来实现故障转移的
uinit64_t currentEpoch;
//集群当前的状态,下线还是上线
int state;
//集群中处理槽的节点的数量
int size;
//集群节点名单(包括Myself)
//使用字典保存,键为节点的名字,值为节点对应的clusterNode结构
dict *nodes;
//…
)clusterState;
总结起来,集群数据结构大概如下所示
每个节点都有ClusterState去保存自己和其他集群节点的状态(ClusterNode),在ClusterNode中又保存着节点之间的连接信息(当前节点与保存的节点)
经过上面的学习,已经知道集群的数据结构是怎样的,下面就看看Cluster Meet命令是怎么实现的
CLUSTER MEET命令的作用是,执行Cluster命令的A节点会将命令中指定的B节点给添加到A节点的集群中去,跟邀请别人进群差不多。
CLUSTER MEET
节点A会先跟B进行握手,以此来确认彼此的存在,并未将来的进一步通信打好基础,下面就来看看握手的过程
- 节点A先为节点B创建一个ClusterNode结构,并将该结构添加到自己的ClusterState.nodes字典里面(现在还不知道节点B的名字,会以一个随机字符串来当节点B的名字,而且flags属性为REDIS_NODE_HANDSHAKE|REDIS_NODE_MEET状态,表明正在握手处理中而且等待MEET信息发送处理)