redis集群实现(三)集群删除节点

Redis集群里的节点支持动态删除,但是一般情况下不会这么做,只有在节点软硬件升级的时候才会主动让节点下线。删除节点的方式就是redis-cli客户端连接到服务器,然后执行cluster forget node-id就可以了,如果是删除一个从节点的话,集群仍然是可用状态,如果是删除一个主节点的话,集群的槽位不足,就会变成不可用状态。

下边看下我在自己的虚拟机运行的例子

[cpp]  view plain  copy
  1. 127.0.0.1:7000> cluster info  
  2. cluster_state:ok  
  3. cluster_slots_assigned:16384  
  4. cluster_slots_ok:16384  
  5. cluster_slots_pfail:0  
  6. cluster_slots_fail:0  
  7. cluster_known_nodes:6  
  8. cluster_size:3  
  9. cluster_current_epoch:8  
  10. cluster_my_epoch:7  
  11. cluster_stats_messages_sent:2058  
  12. cluster_stats_messages_received:1596  
  13.   
  14. 127.0.0.1:7000> cluster nodes  
  15. 930daea84150b5fabd32a95592781b27ceab1b71 192.168.39.153:7001 master - 0 1479044139420 2 connected 5461-10922  
  16. 8a6707d5b9269b6260315b47f300c1ab599733b7 192.168.39.153:7005 slave bdb62bb6ffce71588961f513c74b0d5a1a7145ea 0 1479044141441 6 connected  
  17. bdb62bb6ffce71588961f513c74b0d5a1a7145ea 192.168.39.153:7002 master - 0 1479044139925 3 connected 10923-16383  
  18. 81c884ebfc919ad293f02d797aff1033025ac27e 192.168.39.153:7004 slave 930daea84150b5fabd32a95592781b27ceab1b71 0 1479044140937 2 connected  
  19. 099cfc6fbb785449a8bf5369a53d21a9e127fa42 192.168.39.153:7000 myself,slave a8081e97862d9cf76c72d364f9a173187376f215 0 0 1 connected  
  20. a8081e97862d9cf76c72d364f9a173187376f215 192.168.39.153:7003 master - 0 1479044140430 7 connected 0-5460  

从上边的运行结果可以看出,集群有六个节点,分别是192.168.39.153:7000192.168.39.153:7001192.168.39.153:7002192.168.39.153:7003192.168.39.153:7004192.168.39.153:7005。对应的node-id099cfc6fbb785449a8bf5369a53d21a9e127fa42930daea84150b5fabd32a95592781b27ceab1b71bdb62bb6ffce71588961f513c74b0d5a1a7145eaa8081e97862d9cf76c72d364f9a173187376f21581c884ebfc919ad293f02d797aff1033025ac27e8a6707d5b9269b6260315b47f300c1ab599733b7

然后我们删除从节点192.168.39.153:7004

[cpp]  view plain  copy
  1. 127.0.0.1:7000> cluster forget 81c884ebfc919ad293f02d797aff1033025ac27e  
  2. OK  
  3. 127.0.0.1:7000> cluster info  
  4. cluster_state:ok  
  5. cluster_slots_assigned:16384  
  6. cluster_slots_ok:16384  
  7. cluster_slots_pfail:0  
  8. cluster_slots_fail:0  
  9. cluster_known_nodes:5  
  10. cluster_size:3  
  11. cluster_current_epoch:8  
  12. cluster_my_epoch:7  
  13. cluster_stats_messages_sent:2403  
  14. cluster_stats_messages_received:1941  

可以看到,删除了节点后,cluster_known_nodes显示的值就是5,如果我们输入cluster nodes会发现原先的192.168.39.1537004节点就找不到了,因为他已经从每一个节点的记录中删除了。同事我们也看到cluster_state:ok,说明集群状态仍然是可用的。

那我们尝试着删除主节点192.168.39.1537001看看。

[cpp]  view plain  copy
  1. 127.0.0.1:7000> cluster forget 930daea84150b5fabd32a95592781b27ceab1b71  
  2. OK  
  3. 127.0.0.1:7000> cluster info  
  4. cluster_state:fail  
  5. cluster_slots_assigned:10922  
  6. cluster_slots_ok:10922  
  7. cluster_slots_pfail:0  
  8. cluster_slots_fail:0  
  9. cluster_known_nodes:5  
  10. cluster_size:2  
  11. cluster_current_epoch:8  
  12. cluster_my_epoch:7  
  13. cluster_stats_messages_sent:2627  
  14. cluster_stats_messages_received:2165  

删除了192.168.39.1537001后集群状态就是cluster_state:fail,说明集群此时是不可用的。

我们看看redis源代码,看看forget删除节点是怎么实现的,在redis/cluster.c文件里,客户端传入的forget参数会进入clusterCommand函数

[cpp]  view plain  copy
  1. —————————————————————————————————  
  2.     } else if (!strcasecmp(c->argv[1]->ptr,"forget") && c->argc == 3) {   
  3.         // argv[2]是NODE-ID,查找 NODE-ID 对应的节点  
  4.         clusterNode *n = clusterLookupNode(c->argv[2]->ptr);  
  5.   
  6.         // node-id对应的节点不在集群中,返回错误  
  7.         if (!n) {  
  8.             addReplyErrorFormat(c,"Unknown node %s", (char*)c->argv[2]->ptr);  
  9.             return;  
  10.     //不能删除客户端连接到的服务器自己,也不能删除自己的master  
  11.         } else if (n == myself) {  
  12.             addReplyError(c,"I tried hard but I can't forget myself...");  
  13.             return;  
  14.         } else if (nodeIsSlave(myself) && myself->slaveof == n) {   
  15.             addReplyError(c,"Can't forget my master!");  
  16.             return;  
  17.         }      
  18.   
  19.         // 将节点添加到黑名单  
  20.         clusterBlacklistAddNode(n);  
  21.         // 从集群中删除这个node  
  22.         clusterDelNode(n);  
  23.     //删除后的下一个服务器周期检查会执行更新状态,保存当前集群配置的操作  
  24.         clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|  
  25.                              CLUSTER_TODO_SAVE_CONFIG);  
  26.         addReply(c,shared.ok);  
  27.   
  28.     }  
  29. —————————————————————————————————  

我们继续看clusterBlacklistAddNode函数是如何把node加入到黑名单的

[cpp]  view plain  copy
  1. // 把黑名单中的过期节点删除,把当前node加入到黑名单里  
  2. void clusterBlacklistAddNode(clusterNode *node) {     
  3.     dictEntry *de;  
  4.     sds id = sdsnewlen(node->name,REDIS_CLUSTER_NAMELEN);  
  5.   
  6.     // 查找过期的节点并删除  
  7.     clusterBlacklistCleanup();  
  8.   
  9.     // 把node-id节点添加到黑名单里  
  10.     if (dictAdd(server.cluster->nodes_black_list,id,NULL) == DICT_OK) {  
  11.         id = sdsdup(id);  
  12.     }  
  13.     // 设置node的过期时间  
  14.     de = dictFind(server.cluster->nodes_black_list,id);  
  15.     dictSetUnsignedIntegerVal(de,time(NULL)+REDIS_CLUSTER_BLACKLIST_TTL);  
  16.     sdsfree(id);  
  17. }  

下边是删除节点的关键函数,这个函数首先将所有由这个节点负责的槽位都标记成未分配,然后移除这个节点发送的下线报告,最后释放本节点对这个节点的保存,如果此节点是从节点的话,把此节点的父节点的从节点指针中删除这个节点。

[cpp]  view plain  copy
  1. void clusterDelNode(clusterNode *delnode) {  
  2.     int j;  
  3.     dictIterator *di;  
  4.     dictEntry *de;  
  5.   
  6.     //删除所有向这个节点迁移和被迁移的槽,最后标记为未分配  
  7.     for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {  
  8.         // 取消从此节点迁移槽  
  9.         if (server.cluster->importing_slots_from[j] == delnode)  
  10.             server.cluster->importing_slots_from[j] = NULL;  
  11.         // 取消向此节点迁移槽  
  12.         if (server.cluster->migrating_slots_to[j] == delnode)  
  13.             server.cluster->migrating_slots_to[j] = NULL;  
  14.         // 将所有这个节点负责的槽设置为未分配  
  15.         if (server.cluster->slots[j] == delnode)  
  16.             clusterDelSlot(j);  
  17.     }  
  18.   
  19.     // 移除此节点发送的下线报告  
  20.     di = dictGetSafeIterator(server.cluster->nodes);  
  21.     while((de = dictNext(di)) != NULL) {  
  22.         clusterNode *node = dictGetVal(de);  
  23.   
  24.         if (node == delnode) continue;  
  25.         clusterNodeDelFailureReport(node,delnode);  
  26.     }  
  27.     dictReleaseIterator(di);  
  28.   
  29.     // 将节点从它的主节点的从节点列表中移除  
  30.     if (nodeIsSlave(delnode) && delnode->slaveof)  
  31.         clusterNodeRemoveSlave(delnode->slaveof,delnode);  
  32.   
  33.     // 释放节点  
  34.     freeClusterNode(delnode);  
  35. }  

这样,在本地服务器看来,这个节点就被删除了。集群中的节点会周期性的交换信息,一小段时间以后,整个集群就都知道这个节点的被删除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值