void brpoplpushCommand(redisClient *c) { time_t timeout; if (getTimeoutFromObjectOrReply(c,c->argv[3],&timeout) != REDIS_OK) return; robj *key = lookupKeyWrite(c->db, c->argv[1]); if (key == NULL) { if (c->flags & REDIS_MULTI) { /* Blocking against an empty list in a multi state * returns immediately. */ addReply(c, shared.nullbulk); } else { /* The list is empty and the client blocks. */ blockForKeys(c, c->argv + 1, 1, timeout, c->argv[2]); } } else { if (key->type != REDIS_LIST) { addReply(c, shared.wrongtypeerr); } else { /* The list exists and has elements, so * the regular rpoplpushCommand is executed. */ redisAssertWithInfo(c,key,listTypeLength(key) > 0); rpoplpushCommand(c); } } }
/* Blocking RPOP/LPOP */ void blockingPopGenericCommand(redisClient *c, int where) { robj *o; time_t timeout; int j; if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout) != REDIS_OK) return; for (j = 1; j < c->argc-1; j++) { o = lookupKeyWrite(c->db,c->argv[j]); if (o != NULL) { if (o->type != REDIS_LIST) { addReply(c,shared.wrongtypeerr); return; } else { if (listTypeLength(o) != 0) { /* If the list contains elements fall back to the usual * non-blocking POP operation */ robj *argv[2], **orig_argv; int orig_argc; /* We need to alter the command arguments before to call * popGenericCommand() as the command takes a single key. */ orig_argv = c->argv; orig_argc = c->argc; argv[1] = c->argv[j]; c->argv = argv; c->argc = 2; /* Also the return value is different, we need to output * the multi bulk reply header and the key name. The * "real" command will add the last element (the value) * for us. If this souds like an hack to you it's just * because it is... */ addReplyMultiBulkLen(c,2); addReplyBulk(c,argv[1]); popGenericCommand(c,where); /* Fix the client structure with the original stuff */ c->argv = orig_argv; c->argc = orig_argc; return; } } } } /* If we are inside a MULTI/EXEC and the list is empty the only thing * we can do is treating it as a timeout (even with timeout 0). */ if (c->flags & REDIS_MULTI) { addReply(c,shared.nullmultibulk); return; } /* If the list is empty or the key does not exists we must block */ blockForKeys(c, c->argv + 1, c->argc - 2, timeout, NULL); }
/* Blocking RPOP/LPOP */ void blockingPopGenericCommand(redisClient *c, int where) { robj *o; mstime_t timeout; int j; int slotnum; if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS) != REDIS_OK) return; for (j = 1; j < c->argc-1; j++) { slotnum = keyHashSlot(c->argv[j]->ptr, sdslen(c->argv[j]->ptr)); o = lookupKeyWrite(c->db,c->argv[j],slotnum); if (o != NULL) { if (o->type != REDIS_LIST) { addReply(c,shared.wrongtypeerr); return; } else { if (listTypeLength(o) != 0) { /* Non empty list, this is like a non normal [LR]POP. */ char *event = (where == REDIS_HEAD) ? "lpop" : "rpop"; robj *value = listTypePop(o,where); redisAssert(value != NULL); addReplyMultiBulkLen(c,2); addReplyBulk(c,c->argv[j]); addReplyBulk(c,value); decrRefCount(value); notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event, c->argv[j],c->db->id); if (listTypeLength(o) == 0) { dbDelete(c->db,c->argv[j],slotnum); notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del", c->argv[j],c->db->id); } signalModifiedKey(c->db,c->argv[j],slotnum); server.dirty++; /* Replicate it as an [LR]POP instead of B[LR]POP. */ rewriteClientCommandVector(c,2, (where == REDIS_HEAD) ? shared.lpop : shared.rpop, c->argv[j]); return; } } } } /* If we are inside a MULTI/EXEC and the list is empty the only thing * we can do is treating it as a timeout (even with timeout 0). */ if (c->flags & REDIS_MULTI) { addReply(c,shared.nullmultibulk); return; } /* If the list is empty or the key does not exists we must block */ blockForKeys(c, c->argv + 1, c->argc - 2, timeout, NULL); }
/* Blocking RPOP/LPOP */ static void blockingPopGenericCommand(redisClient *c, int where) { robj *o; time_t timeout; int j; for (j = 1; j < c->argc-1; j++) { o = lookupKeyWrite(c->db,c->argv[j]); if (o != NULL) { if (o->type != REDIS_LIST) { addReply(c,shared.wrongtypeerr); return; } else { list *list = o->ptr; if (listLength(list) != 0) { /* If the list contains elements fall back to the usual * non-blocking POP operation */ robj *argv[2], **orig_argv; int orig_argc; /* We need to alter the command arguments before to call * popGenericCommand() as the command takes a single key. */ orig_argv = c->argv; orig_argc = c->argc; argv[1] = c->argv[j]; c->argv = argv; c->argc = 2; /* Also the return value is different, we need to output * the multi bulk reply header and the key name. The * "real" command will add the last element (the value) * for us. If this souds like an hack to you it's just * because it is... */ addReplySds(c,sdsnew("*2\r\n")); addReplyBulk(c,argv[1]); popGenericCommand(c,where); /* Fix the client structure with the original stuff */ c->argv = orig_argv; c->argc = orig_argc; return; } } } } /* If the list is empty or the key does not exists we must block */ timeout = strtol(c->argv[c->argc-1]->ptr,NULL,10); if (timeout > 0) timeout += time(NULL); blockForKeys(c,c->argv+1,c->argc-2,timeout); }
// BRPOPLPUSH source destination timeout // BRPOPLPUSH命令的实现 void brpoplpushCommand(client *c) { mstime_t timeout; //以秒为单位取出超时时间 if (getTimeoutFromObjectOrReply(c,c->argv[3],&timeout,UNIT_SECONDS) != C_OK) return; //以写操作读取出 source的值 robj *key = lookupKeyWrite(c->db, c->argv[1]); //如果键为空,阻塞 if (key == NULL) { // 如果命令在一个事务中执行,则发送一个空回复以避免死等待 if (c->flags & CLIENT_MULTI) { /* Blocking against an empty list in a multi state * returns immediately. */ addReply(c, shared.nullbulk); } else { /* The list is empty and the client blocks. */ // 列表为空,则将client阻塞 blockForKeys(c, c->argv + 1, 1, timeout, c->argv[2]); } //如果键不为空,指向RPOPLPUSH } else { //判断取出的value对象是否为列表类型,不是的话发送类型错误信息 if (key->type != OBJ_LIST) { addReply(c, shared.wrongtypeerr); } else { /* The list exists and has elements, so * the regular rpoplpushCommand is executed. */ // value对象的列表存在且有元素,所以调用普通的rpoplpush命令 serverAssertWithInfo(c,key,listTypeLength(key) > 0); rpoplpushCommand(c); } } }
// BRPOP BLPOP 命令的底层实现 // BLPOP key [key ...] timeout void blockingPopGenericCommand(client *c, int where) { robj *o; mstime_t timeout; int j; // 以秒为单位取出timeout值 if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS) != C_OK) return; //遍历所有的key for (j = 1; j < c->argc-1; j++) { //以写操作取出当前key的值 o = lookupKeyWrite(c->db,c->argv[j]); // value对象不为空 if (o != NULL) { // 如果value对象的类型不是列表类型,发送类型错误信息 if (o->type != OBJ_LIST) { addReply(c,shared.wrongtypeerr); return; } else { // 列表长度不为0 if (listTypeLength(o) != 0) { /* Non empty list, this is like a non normal [LR]POP. */ // 保存事件名称 char *event = (where == LIST_HEAD) ? "lpop" : "rpop"; // 保存弹出的value对象 robj *value = listTypePop(o,where); serverAssert(value != NULL); // 发送回复给client addReplyMultiBulkLen(c,2); addReplyBulk(c,c->argv[j]); addReplyBulk(c,value); // 释放value decrRefCount(value); // 发送事件通知 notifyKeyspaceEvent(NOTIFY_LIST,event, c->argv[j],c->db->id); //如果弹出元素后列表为空 if (listTypeLength(o) == 0) { //从数据库中删除当前的key dbDelete(c->db,c->argv[j]); // 发送"del"的事件通知 notifyKeyspaceEvent(NOTIFY_GENERIC,"del", c->argv[j],c->db->id); } //数据库的键被修改,发送信号 signalModifiedKey(c->db,c->argv[j]); //更新脏键 server.dirty++; /* Replicate it as an [LR]POP instead of B[LR]POP. */ // 传播一个[LR]POP 而不是B[LR]POP rewriteClientCommandVector(c,2, (where == LIST_HEAD) ? shared.lpop : shared.rpop, c->argv[j]); return; } } } } /* If we are inside a MULTI/EXEC and the list is empty the only thing * we can do is treating it as a timeout (even with timeout 0). */ // 如果命令在一个事务中执行,则发送一个空回复以避免死等待 if (c->flags & CLIENT_MULTI) { addReply(c,shared.nullmultibulk); return; } /* If the list is empty or the key does not exists we must block */ // 参数中的所有键都不存在,则阻塞这些键 blockForKeys(c, c->argv + 1, c->argc - 2, timeout, NULL); }