问题:
redis可以设置一个策略,当内存达到一个给定的阈值的时候,按照给定的规则淘汰key,详情参考http://redis.io/topics/lru-cache。现在给redis增加一条命令,可以查询被淘汰的key,示例如下:
>evitedkeys
1)key1
2)key2
3)key3
...
注意:
redis版本是3.0.7
测试的环境是Ubuntu 14.04 LTS
思路:
第一步:给redis添加一条命令需要在哪些地方加代码?
第二步:找到根据某个策略删除key的代码位置,在删除那个key之前将它存到外存的一个文件中;
第三步:增加一条命令,读取外存中的那个文件获得被删除的keys,并按照需要的格式输出。
{"sismember",sismemberCommand,3,"rF",0,NULL,1,1,1,0,0}
sismember是命令名,sismemberCommand是具体实现,3是参数的个数,“rF“关键字具体含义见代码上方的注释,后面几个数字表示第一个参数的位置,最后一个参数的位置,参数间的间隔等。
void sismemberCommand(redisClient *c);
这里明显是sismemberCommand的声明,
void sismemberCommand(redisClient *c) {
robj *set;
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
c->argv[2] = tryObjectEncoding(c->argv[2]);
if (setTypeIsMember(set,c->argv[2]))
addReply(c,shared.cone);
else
addReply(c,shared.czero);
}
这里明显是sismemberCommand的实现。
{"evitedkeys",evitedkeysCommand,1,"rF",0,NULL,0,0,0,0,0}
在redis.h中,我们声明这个函数:
void evitedkeysCommand(redisClient *c);
最后在redis.c中,实现这个函数(这里先是空实现,后面再加上具体的实现方法):
void evitedkeysCommand(redisClient *c){}
src/redis-server
切换到另一个命令行,运行
redis-cli
输入
evitedkeys aa
得到的回复是
(error) ERR wrong number of arguments for 'evitedkeys' command
第二步;找到根据某个策略删除key的代码位置,在删除那个key之前将它存到外存的一个文件中。
int freeMemoryIfNeeded(void) {
size_t mem_used, mem_tofree, mem_freed;
int slaves = listLength(server.slaves);
mstime_t latency, eviction_latency;
/* Remove the size of slaves output buffers and AOF buffer from the
* count of used memory. */
mem_used = zmalloc_used_memory();
......
/* volatile-random and allkeys-random policy */
....
/* volatile-lru and allkeys-lru policy */
....
/* Finally remove the selected key. */
....
}
//在删除bestkey之前将它存进外存文件evitedkeysRecord中。由于之后在用fread的时候需要确定读取的字节数,所以这里也把bestkey的长度一起存起来
FILE* evitedkeysFile = fopen("evitedkeysRecord","ab+");
fseek(evitedkeysFile,0,SEEK_END);
int lengthOfEvitedkey = strlen(bestkey);
int *ptr = &lengthOfEvitedkey;
fwrite(ptr,sizeof(int),1,evitedkeysFile);
fwrite(bestkey,sizeof(char),strlen(bestkey)+1,evitedkeysFile);
fclose(evitedkeysFile);
1)key1
2)key2
3)key3
这种格式输出?void mgetCommand(redisClient *c) {
int j;
addReplyMultiBulkLen(c,c->argc-1);
for (j = 1; j < c->argc; j++) {
robj *o = lookupKeyRead(c->db,c->argv[j]);
if (o == NULL) {
addReply(c,shared.nullbulk);
} else {
if (o->type != REDIS_STRING) {
addReply(c,shared.nullbulk);
} else {
addReplyBulk(c,o);
}
}
}
}
进一步深入,其中的addReplyMultiBulkLen,addReply,addReplyBulk函数的定义中,又会调用更多我不认识的函数,分析起来比较复杂。
void configGetCommand(redisClient *c) {
robj *o = c->argv[2];
void *replylen = addDeferredMultiBulkLength(c);
char *pattern = o->ptr;
char buf[128];
int matches = 0;
redisAssertWithInfo(c,o,sdsEncodedObject(o));
/* String values */
config_get_string_field("dbfilename",server.rdb_filename);
config_get_string_field("requirepass",server.requirepass);
config_get_string_field("masterauth",server.masterauth);
config_get_string_field("unixsocket",server.unixsocket);
config_get_string_field("logfile",server.logfile);
config_get_string_field("pidfile",server.pidfile);
......
setDeferredMultiBulkLength(c,replylen,matches*2);
}
其中的config_get_string_field函数其实是一个宏定义:
#define config_get_string_field(_name,_var) do { \
if (stringmatch(pattern,_name,0)) { \
addReplyBulkCString(c,_name); \
addReplyBulkCString(c,_var ? _var : ""); \
matches++; \
} \
} while(0);
看了上面两个代码块我们基本上能明白如何给客户端输出多行结果了:先用addDeferredMultibulkLength表示要输出多行命令,接着多次用addReplyBulkCString将每行输出的字符串放进缓存中,最后用setDeferredMultiBulkLength来发送输出缓存中的所有内容。于是evitedkeysCommand的实现代码如下:
void evitedkeysCommand(redisClient *c){
void *replylen = addDeferredMultiBulkLength(c);
unsigned long numOfEvitedkeys = 0;
FILE *readEvitedkeysFile = fopen("evitedkeysRecord","ab+");
fseek(readEvitedkeysFile,0,SEEK_SET);
int lengthOfKey;
while(fread(&lengthOfKey,sizeof(int),1,readEvitedkeysFile)){
//redis每个key的最大命名长度是1024字节。这里也可以用用malloc来动态分配内存。但是用malloc的话,编译的时候会报警
//如果用malloc下面一行代码就换成char *evitedkey = (char*)malloc(lengthOfKey*sizeof(char)+1);
char evitedkey[1025];
if(fread(evitedkey,sizeof(char),lengthOfKey*sizeof(char)+1,readEvitedkeysFile)){
addReplyBulkCString(c,evitedkey);
numOfEvitedkeys++;}
}
setDeferredMultiBulkLength(c,replylen,numOfEvitedkeys);
fclose(readEvitedkeysFile);
}