1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| 1. link->node 是什么?我目前能理解到的是主动建立连接的一方才会正确设置这个值,监听接收消息的一方这个值为NULL。也许是用来区分这2种情况的吧。 2. 如果 link->node 有值(主动建立连接方),则:
如果在握手状态(nodeInHandshake(link->node)),并且发送者是已知的节点,先会更新已知节点的信息(如果ip/port 有变化,link 相同认为无变化),然后则删除 link->node 这个节点的所有相关的信息,包括节点自身的信息。(包括slot / importing\_slots\_from/ migrating\_slots\_to/ node->fail_reports 等等)。清理完之后,此函数就返回,不执行后续逻辑。这里面有一个很关键的点,sender 是根据收到的消息头中的hdr->sender(node ID)在 server.cluster->nodes 列表中查找到的节点,而 link->node 是与当前连接关联的节点,可能在大多数情况是相同的,但是在最初节点A向节点B发送MEET消息时,并不知道节点B的node ID 所以会随机生成一个,在A收到B的回复PONG时,B会携带自己真实的node ID,这时这2个值就不同了。如果相同就表明,这个节点B已经在节点A的已知列表,相当于实际上的一个节点,存在了2份,重复了,就执行删除正在链接的节点。这里可能又会有疑问了,已经在A的已知列表,为啥还要再向B发送MEET消息?这个就要说起 CLUSTER MEET命令,带有相同ip /port 的 MEET 命令可以多次执行,但是如果上一个MEET命令已经与对应节点建立连接完成,这个MEET消息就会重复发送,就是这里的情况,如果上一个MEET命令与对应的节点还在握手状态,则会立即返回,忽略这条MEET命令,这种情况MEET消息就不会重复发送。还有一种情况也可能会出现这样的问题,在执行了CLUSTER MEET 命令后,与实际发送MEET消息给对应节点这中间是有时间间隔的,在这个时间窗口,当前节点可能会收到其他节点已经把这个节点携带过来了,这时也就重复了,所以这里的这个排重逻辑还是少不了。 如果在握手状态,发送者是未知节点,这应该就是正常的收到PONG回复的情况了。重命名节点的名称(node->name 也就是 Node ID )。移除 CLUSTER\_NODE\_HANDSHAKE 标记。增加 CLUSTER\_NODE\_MASTER 、 CLUSTER\_NODE\_SLAVE 标记。因为不知道是 slave 还是master,所以都加上,后续再移除。 如果不是握手状态,那么就比较连接的节点名称与消息中包含的节点名称是否相同,如果不同,则断开这个连接,link->node->ip / link->node->port / link->node->cport 设置为 0 值,link->node->flags 增加 CLUSTER\_NODE\_NOADDR 标记。然后函数就立即返回了。
3. 如果发送者是已知节点(sender 有值),同步消息中的 CLUSTER\_NODE\_NOFAILOVER 标记。 4. 如果发送者是已知节点(sender 有值),不是握手状态,并且是PING消息,则 如果有必要的话更新节点信息(ip/ port /c port) 5. 如果 link->node 有值,并且是 PONG消息时,这是一个正常的 ping <-> pong 流程的回复。
设置pong接收时间 link->node->pong_received = mstime() ,将会根据这个时间,时间越小,会优先再次发送 ping 消息来检测节点状态。 设置ping发送时间 link->node->ping\_sent = 0 , 设置为0 表明之前发送的ping已经收到pong回复,说明节点正常,否则将会根据这个ping\_send 的时间来判断是否超时。 收到pong回复,说明节点正常。如果之前有标记PFAIL(可能失败),则移除。 link->node->flags &= ~CLUSTER\_NODE\_PFAIL。如果之前有标记FAIL(失败),满足一定条件则移除这个标记,并不总是直接移除,Slave节点与Master节点的处理逻辑不同。
6. 如果是已知节点(sender 有值),接下来将处理 slave -> master 或者 master -> slave 角色转换的情况
根据 hdr->slaveof 来判断发送者角色是master还是 slave,如果为空,则认为是master节点。如果当前节点已经保存的角色与发送者最新的角色不同,则认为是发生了角色变化。如果同样是slave,但是对应的master节点不同的话也同样需要更新。因此分为了4种情况:a. slave -> master, b. master->slave, c.一直是slave,但是对应的master节点不同,d. 无变化。但是在这里代码结构稍有不同,是按照最新的状态的不同而分开处理。如下: 最新是master角色
slave -> master 需要把原来master的slave列表中删除这个节点,添加 CLUSTER_NODE_MIGRATE_TO 标记 清除 CLUSTER_NODE_SLAVE 标记 添加 CLUSTER_NODE_MASTER 标记 清除 slaveof ,n->slaveof = NULL
最新是slave角色
master -> slave 删除 server.cluster->slots 中对应的这个节点。移除CLUSTER_NODE_MASTER CLUSTER_NODE_MIGRATE_TO 标记。添加 CLUSTER_NODE_SLAVE 标记。 对应的master节点不同。需要把原来master的slave列表中删除这个节点,然后配置新的主从关系。
通过上面 2、3 的 CLUSTER\_NODE\_SLAVE 、CLUSTER\_NODE\_MASTER 标记的处理,最后只保留了其中一个标记,也就是要么是slave、要么是master。
7. 更新关于slot的信息
当然这一步也需要发送者是已知节点,并且是在设置 master / slave 状态之后。这是因为这一步会使用到 master / slave 状态,如果在这之前发送者消息中声称的状态与当前节点已经保存的状态不同,则无法正确处理。 发送者消息中声称的slot信息与当前节点已经保存的这个发送者负责的slot不是完全相同。可能是发送者消息中声称的更新,也可能是当前节点已知的更新。两种情况分别处理。 如果发送者是master,则使用其下保存的slot信息,如果是slave则使用其对于master的slot信息,这样为的是取到最新的slot信息。 如果消息中包含的slot信息与当前保存的slot信息不同,并且这个slot对应的节点版本更大,则更新当前保存的slot信息(clusterUpdateSlotsConfigWith)。这里只考虑发送者是master的情况,那么slave保存的slot 何时更新呢??? 如果与上面的情况相反,则发送消息给发送者(UPDATE类型的消息) clusterUpdateSlotsConfigWith 函数执行逻辑在其他小节中有提到,这里不再重复。
8. 解决epoch相同的情况
只有发送者与当前节点都为 master ,并且configEpoch相同时才执行此项处理(senderConfigEpoch == myself->configEpoch) 比较节点名称,节点名称比较大的节点不执行任何处理。因此只有在当前节点的名称小于发送者的名称时才会更新server.cluster->currentEpoch++ 与 myself->configEpoch = server.cluster->currentEpoch
9. 如果发送者已知,则执行clusterProcessGossipSection,处理 gossip部分。(这部分将单独展开)
|