redis cluster 内部通信机制
1、基础通信原理
(1)redis cluster节点间采取gossip协议进行通信
跟集中式不同,不是将集群元数据(节点信息,故障,等等)集中存储在某个节点上,而是互相之间不断通信,保持整个集群所有节点的数据是完整的。
维护集群的元数据常用两种方法,一种是集中式,另一种叫做gossip。
集中式:好处在于,元数据的更新和读取,时效性非常好,一旦元数据出现了变更,立即就更新到集中式的存储中,其他节点读取的时候立即就可以感知到; 不好在于,所有的元数据的跟新压力全部集中在一个地方,可能会导致元数据的存储有压力。
gossip:好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力; 缺点,元数据更新有延时,可能导致集群的一些操作会有一些滞后
我们刚才做reshard,去做另外一个操作,会发现说,configuration error,达成一致。
(2)10000端口
每个节点都有一个专门用于节点间通信的端口,就是自己提供服务的端口号+10000,比如7001,那么用于节点间通信的就是17001端口。
每个节点每隔一段时间都会往另外几个节点发送ping消息,同时其他节点接收到ping之后返回pong。
(3)交换的信息
故障信息,节点的增加和移除,hash slot信息,等等。
2、gossip协议
gossip协议:所有节点都持有一份元数据,不同的节点如果出现了元数据的变更之后,就不断将元数据发送给其他的节点,让其他及诶单也进行元数据的变更。
gossip协议包含多种消息,包括ping,pong,meet,fail,等等。
3、ping消息
ping很频繁,而且要携带一些元数据,所以可能会加重网络负担。
每个节点每秒会执行10次ping,每次会选择5个最久没有通信的其他节点,当然如果发现某个节点通信延时达到了cluster_node_timeout / 2,那么立即发送ping,避免数据交换延时过长,如果落后的时间太长了,如两个节点之间超过5分钟没有交换数据,那么整个集群处于严重的元数据不一致,这样整个集群会出现严重问题。所以cluster_node_timeout可以调节,如果调节比较大,那么会降低发送的频率。
每次ping,都会带上自己节点的信息,还带上其他节点的信息(至少包含3个其他节点的信息,最多包含总节点-2个其他节点的信息)发送出去,进行数据交换。
集群发现:MEET
最开始时,每个Redis实例自己是一个集群,我们通过cluster meet让各个结点互相握手(handshake)。这也是Redis Cluster目前的一个缺点:**缺少节点的自动发现功能。**
组建一个真正的可工作的集群,必须将各个独立的节点连接起来,构成一个包含多个节点的集群。
连接各个节点的工作使用CLUSTER MEET命令来完成。
CLUSTER MEET <ip> <port>
CLUSTER MEET命令实现:
1、节点Node1会为节点Node2创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。
2、节点Node1根据CLUSTER MEET命令给定的IP地址和端口号,向节点Node2发送一条MEET消息。
3、节点Node2接收到节点Node1发送的MEET消息,节点Node2会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。
4、节点Node2向节点Node1返回一条PONG消息。
节点Node1将受到节点Node2返回的PONG消息,通过这条PONG消息节点Node1可以知道节点Node2已经成功的接收了自己发送的MEET消息。
5、之后,节点Node1将向节点Node2返回一条PING消息。
节点Node2将接收到的节点Node1返回的PING消息,通过这条PING消息节点Node2可以知道节点Node1已经成功的接收到了自己返回的PONG消息,握手完成。
6、之后,节点Node1会将节点Node2的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与节点Node2进行握手,最终,经过一段时间后,节点Node2会被集群中的所有节点认识。
集群消息处理clusterProcessPacket
通过集群消息处理函数 clusterProcessPacket处理消息:
1、更新接收消息计数器。
2、查找发送者节点并且不是handshake节点。
3、更新自己的epoch和slave的offset信息。
4、处理MEET消息,使加入集群。
5、从goosip中发现未知节点,发起handshake。
6、对PING,MEET回复PONG。
7、根据收到的心跳信息更新自己clusterState中的master-slave,slots信息。
8、对FAILOVER_AUTH_REQUEST消息,检查并投票。
9、处理FAIL,FAILOVER_AUTH_ACK,UPDATE信息。
定时任务clusterCron
1、对handshake节点建立Link,发送Ping或Meet。
2、向随机几点发送Ping。
3、查看是否需要做Failover。
4、统计并决定是否进行slave的迁移,来平衡不同master的slave数。
5、判断所有pfail报告数是否过半数。
心跳数据
发送消息头信息Header(所负责slots的信息、主从信息、ip port信息、状态信息)。
发送其他节点Gossip信息(ping_sent、 pong_received、ip,、port信息、状态信息,比如发送者认为该节点已经不可达,会在状态信息中标记其为PFAIL或FAIL)。
clusterMsg结构的currentEpoch、sender、myslots等属性记录了发送者自身的节点信息,接收者会根据这些信息,在自己的clusterState.nodes字典里找到发送者对应的clusterNode结构,并对结构进行更新。
Redis集群中的各个节点通过Gossip协议来交换各自关于不同节点的状态信息,其中**Gossip协议由MEET、PING、PONG三种消息实现**,这三种消息的正文都由两个clusterMsgDataGossip结构组成。
1、每次发送MEET、PING、PONG消息时,发送者都从自己的已知节点列表中随机选出两个节点(可以是主节点或者从节点),并将这两个被选中节点的信息分别保存到两个结构中。
2、当接收者收到消息时,接收者会访问消息正文中的两个结构,并根据自己是否认识clusterMsgDataGossip结构中记录的被选中节点进行操作:
(1)如果被选中节点不存在于接收者的已知节点列表,那么说明接收者是第一次接触到被选中节点,接收者将根据结构中记录的IP地址和端口号等信息,与被选择节点进行握手。
(2)如果被选中节点已经存在于接收者的已知节点列表,那么说明接收者之前已经与被选中节点进行过接触,接收者将根据clusterMsgDataGossip结构记录的信息,对被选中节点对应的clusterNode结构进行更新。
5.5.6. 数据结构
1、clusterNode结构
clusterNode结构保存了一个节点的当前状态,比如节点的创建时间,节点的名字,节点当前的配置纪元,节点的IP和地址等。
1)slots:位图,由当前clusterNode负责的slot为1。
2)salve, slaveof:主从关系信息。
3)ping_sent, pong_received:心跳包收发时间。
4)clusterLink *link:节点间的连接。
5)list *fail_reports:收到的节点不可达投票。
2、clusterState结构
clusterState结构记录了在当前节点的集群目前所处的状态。
1)myself:指针指向自己的clusterNode。
2)currentEpoch:当前节点的最大epoch,可能在心跳包的处理中更新。
3)nodes:当前节点记录的所有节点,为clusterNode指针数组。
4)slots:slot与clusterNode指针映射关系。
5)migrating_slots_to,importing_slots_from:记录slots的迁移信息。
6)failover_auth_time,failover_auth_count,failover_auth_sent,failover_auth_rank,failover_auth_epoch:Failover相关信息。