为什么创建 Redis 集群时会自动错开主从节点?( 二 )


为什么创建 Redis 集群时会自动错开主从节点?

文章插图
从上面的代码可以看到 , 如果得分为 0  , 说明反亲和性已经很好 , 无需优化 。直接跳到 cleanup 去释放 offenders 节点的内存空间 。
如果得分不为 0  , 则就会设置一个最大迭代次数maxiter , 这个次数跟节点的数量成正比 , 然后 while 循环在有限次迭代内进行优化操作 。
为什么创建 Redis 集群时会自动错开主从节点?

文章插图
这个函数的核心就在 while 循环里 , 我们来看一下其中的一些片段:
首先 offending_len 来存储违反规则的节点数 , 然后如果之前有违反规则的节点(offenders != NULL)则释放掉(zfree(offenders));
然后重新计算得分 , 如果得分为0或没有违反规则的节点 , 退出 while 循环;
为什么创建 Redis 集群时会自动错开主从节点?

文章插图
接着去随机选择一个违反规则的节点 , 尝试交换分配的 master:
为什么创建 Redis 集群时会自动错开主从节点?

文章插图
然后遍历集群中的节点 , 寻找能够交换 master 的 slave 。如果没有找到 , 那就退出循环:
为什么创建 Redis 集群时会自动错开主从节点?

文章插图
如果找到了 , 就开始交换并计算交换后的反亲和性得分:
为什么创建 Redis 集群时会自动错开主从节点?

文章插图
如果交换后的得分比之前的得分还大 , 说明白交换了 , 还不如不交换 , 就会回顾;如果交换后的得分比之前的得分小 , 说明交换是没毛病的:
为什么创建 Redis 集群时会自动错开主从节点?

文章插图
最后释放资源 , 准备下一次 while 循环:
为什么创建 Redis 集群时会自动错开主从节点?

文章插图
总结一下:
每次 while 循环会尝试随机选择一个违反反亲和性规则的从节点 , 并与另一个随机选中的从节点交换其主节点分配 , 然后重新计算交换后的反亲和性得分 如果交换后的得分变大 , 说明交换不利于反亲和性 , 会回滚交换 如果交换后得分变小 , 则保持 , 后面可能还需要多次交换 这样 , 通过多次随机的交换尝试 , 最终可以达到更好的反亲和性分布最后则是一些收尾工作 , 像输出日志信息 , 释放内存等 , 这里不过多介绍:
为什么创建 Redis 集群时会自动错开主从节点?

文章插图
下面是完整代码:
static void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes, int ip_count) { clusterManagerNode **offenders = NULL; int score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count, NULL, NULL); if (score == 0) goto cleanup; clusterManagerLogInfo(">>> Trying to optimize slaves allocation " "for anti-affinityn"); int node_len = cluster_manager.nodes->len; int maxiter = 500 * node_len; // Effort is proportional to cluster size... srand(time(NULL)); while (maxiter > 0) { int offending_len = 0; if (offenders != NULL) { zfree(offenders); offenders = NULL; } score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count, &offenders, &offending_len); if (score == 0 || offending_len == 0) break; // Optimal anti affinity reached /* We'll try to randomly swap a slave's assigned master causing * an affinity problem with another random slave, to see if we * can improve the affinity. */ int rand_idx = rand() % offending_len; clusterManagerNode *first = offenders[rand_idx], *second = NULL; clusterManagerNode **other_replicas = zcalloc((node_len - 1) * sizeof(*other_replicas)); int other_replicas_count = 0; listIter li; listNode *ln; listRewind(cluster_manager.nodes, &li); while ((ln = listNext(&li)) != NULL) { clusterManagerNode *n = ln->value; if (n != first && n->replicate != NULL) other_replicas[other_replicas_count++] = n; } if (other_replicas_count == 0) { zfree(other_replicas); break; } rand_idx = rand() % other_replicas_count; second = other_replicas[rand_idx]; char *first_master = first->replicate, *second_master = second->replicate; first->replicate = second_master, first->dirty = 1; second->replicate = first_master, second->dirty = 1; int new_score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count, NULL, NULL); /* If the change actually makes thing worse, revert. Otherwise * leave as it is because the best solution may need a few * combined swaps. */ if (new_score > score) { first->replicate = first_master; second->replicate = second_master; } zfree(other_replicas); maxiter--; } score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count, NULL, NULL); char *msg; int perfect = (score == 0); int log_level = (perfect ? CLUSTER_MANAGER_LOG_LVL_SUCCESS : CLUSTER_MANAGER_LOG_LVL_WARN); if (perfect) msg = "[OK] Perfect anti-affinity obtAIned!"; else if (score >= 10000) msg = ("[WARNING] Some slaves are in the same host as their master"); else msg=("[WARNING] Some slaves of the same master are in the same host"); clusterManagerLog(log_level, "%sn", msg); cleanup: zfree(offenders); }


推荐阅读