扩展redis cluster addslots 命令
众所周知,redis集群集群模式下必须将16384个指派完,也就是16384个槽点都在处理时,集群才处于上线上线状态。在指派槽时用的命令是这样的 “cluster addslots 1 2 3” 这样。这里有个不好的地方就是每次添加只能一个槽点一个槽点这样添加,着实不方便,我想扩展一下redis的这个命令,令他支持""cluster addslots {1…5000} "这样的语法
我们先看一下redis中 处理“ cluster addslots”命令的相关的代码,在 cluster.c的clusterCommand方法中,
具体代码如下(为了便于阅读我会添加一些中文注释以便于读者理解):
else if ((!strcasecmp(c->argv[1]->ptr,"addslots") ||
!strcasecmp(c->argv[1]->ptr,"delslots")) && c->argc >= 3)
{ //如果是"cluster addslots或者 cluster "delslots命令
/* CLUSTER ADDSLOTS <slot> [slot] ... */
/* CLUSTER DELSLOTS <slot> [slot] ... */
int j, slot;
unsigned char *slots = zmalloc(CLUSTER_SLOTS); //一个数组,记录所有要添加或者删除的槽 slots[519] == 1 代表 519槽位置要删除或者添加
int del = !strcasecmp(c->argv[1]->ptr,"delslots"); //是否是删除命令
memset(slots,0,CLUSTER_SLOTS); //常规操作
/* Check that all the arguments are parseable and that all the
* slots are not already busy. */
/*遍历参数列表 比如cluster addslots 1 2 3 4就是遍历 1 2 3 4 */
for (j = 2; j < c->argc; j++) {
if ((slot = getSlotOrReply(c,c->argv[j])) == -1) {
zfree(slots);
return;
}
if (del && server.cluster->slots[slot] == NULL) {
addReplyErrorFormat(c,"Slot %d is already unassigned", slot);
zfree(slots);
return;
} else if (!del && server.cluster->slots[slot]) {
addReplyErrorFormat(c,"Slot %d is already busy", slot);
zfree(slots);
return;
}
if (slots[slot]++ == 1) {
addReplyErrorFormat(c,"Slot %d specified multiple times",
(int)slot);
zfree(slots);
return;
}
}
//处理所有输入 slot
for (j = 0; j < CLUSTER_SLOTS; j++) {
if (slots[j]) {
int retval;
/* If this slot was set as importing we can clear this
* state as now we are the real owner of the slot. */
if (server.cluster->importing_slots_from[j])
server.cluster->importing_slots_from[j] = NULL;
retval = del ? clusterDelSlot(j) :
clusterAddSlot(myself,j);
serverAssertWithInfo(c,NULL,retval == C_OK);
}
}
zfree(slots);
clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);
addReply(c,shared.ok);
}
然后改写:
else if ((!strcasecmp(c->argv[1]->ptr,"addslots") ||
!strcasecmp(c->argv[1]->ptr,"delslots")) && c->argc >= 3)
{
int j;
int del = !strcasecmp(c->argv[1]->ptr,"delslots");
for (j = 2; j < c->argc; j++) {
addOrDelSlots(c,c->argv[j],del);
}
}
void addOrDelSlots(redisClient *c,robj *o,int del){
int j;
char *arg = o->ptr;
// 一个数组,记录所有要添加或者删除的槽
unsigned char *slots = zmalloc(REDIS_CLUSTER_SLOTS);
// 将 slots 数组的所有值设置为 0
memset(slots,0,REDIS_CLUSTER_SLOTS);
if (strstr(arg,"{") == arg && strrchr(arg,'}') == (arg + strlen(arg) - 1)) //是否以"{"开头"}"结尾 这是区间添加参数如"{0...1000}"
{
char *ellipsisIndex; //省略号下标即"..."
char startNum[6], endNum[6],startNumTemp[6],endNumTemp[6]; //because of slotNums maximum is "16384"
int start, end;
memset(startNum,0,6);
memset(endNum,0,6);
memset(startNumTemp,0,6);
memset(endNumTemp,0,6);
if( (ellipsisIndex = strstr(arg,"...")) == NULL){ //客户端格式输入错误
addReplyErrorFormat(c,"cluster addslots format error %d", ellipsisIndex);
return;
}
memcpy(startNum,arg + 1,ellipsisIndex - arg - 1); //填充startNum
memcpy(endNum,ellipsisIndex + 3,strlen(ellipsisIndex + 3) - 1); //填充endNum
start = atoi(startNum);
end = atoi(endNum);
sprintf(startNumTemp,"%d",start);
sprintf(endNumTemp,"%d",end);
if (strcmp(startNum,startNumTemp) || strcmp(endNum,endNumTemp))
{
addReplyErrorFormat(c,"cluster addslots format error");
return;
}
if(start <= -1 || end >= REDIS_CLUSTER_SLOTS || start > end){
addReplyErrorFormat(c,"cluster addslots out of the");
return;
}
for(;start <= end; start++)
slots[start] = 1;
}else{
int slot;
// 获取 slot 数字
if ( (slot = getSlotOrReply(c,o)) == - 1) {
zfree(slots);
return;
}
slots[slot] = 1;
}
// 处理所有输入 slot
for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {
if (slots[j]) {
int retval;
if (del && server.cluster->slots[j] == NULL) {
addReplyErrorFormat(c,"Slot %d is already unassigned", j);
zfree(slots);
return;
}else if (!del && server.cluster->slots[j]) { // 如果这是 addslots 命令,并且槽已经有节点在负责,那么返回一个错误
addReplyErrorFormat(c,"Slot %d is already busy", j);
zfree(slots);
return;
}
/* If this slot was set as importing we can clear this
* state as now we are the real owner of the slot. */
// 如果指定 slot 之前的状态为载入状态,那么现在可以清除这一状态
// 因为当前节点现在已经是 slot 的负责人了
if (server.cluster->importing_slots_from[j])
server.cluster->importing_slots_from[j] = NULL;
// 添加或者删除指定 slot
retval = del ? clusterDelSlot(j) :
clusterAddSlot(myself,j);
redisAssertWithInfo(c,NULL,retval == REDIS_OK);
}
}
zfree(slots);
clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);
addReply(c,shared.ok);
}
这样就可以支持范围添加了像这样:cluster addslots {0…100}了。这段代码没有太大的把握保证没有bug(保不准可能各种出现段错误),我只用过一些测试案例进行测试过,貌似还行,这段代码质量和风格其实不怎么样。可能以后我会改写优化这段代码,或许吧。
之后的时间里我会写一些关于redis,JDK,和一些开源框架的文章,全都是源码级别的,从源码层面搞懂原理,从源码中学习数据结构,设计模式,算法。