概述
经过上一篇文章的分析,我们已经知道 redis 是如何处理 client 的请求,解析处一个完整的 command,进一步处理这个 command,并且向 client 发送响应。今天来学习一下 redis 的 command 相关代码。
redisCommand
struct redisCommand {
// 命令名字
char *name;
// command 对应的处理函数
redisCommandProc *proc;
// 参数个数,如果是负数,代表参数个数需要大于 -arity
int arity;
// 字符串表示的 FLAG
char *sflags;
// 实际 FLAG,有如下几个:
/*
w: 写入
r: 读
m: 可能会分配内存,当服务器已经处于 oom 时禁止调用
a: 管理员命令,比如 SAVE
p: PUB/SUB 相关命令
f: 与 aof 相关,后续再说
s: 不允许在脚本中使用的命令
R: 随机命令,相同输入的结果可能不同
还有几个本文不涉及,略过
*/
int flags;
// 从命令中判断命令的键参数。在 Redis 集群转向时使用。
redisGetKeysProc *getkeys_proc;
// 指定哪些参数是 key
int firstkey; /* The first argument that's a key (0 = no keys) */
int lastkey;
/* The last argument that's a key */
int keystep;
/* The step between first and last key */
// 统计字段,耗时、调用次数
long long microseconds, calls;
};
在 src/redis.c 文件中,有一个 commandTable,保存了所有 redis 支持的命令:
struct redisCommand redisCommandTable[] = {
{"get",getCommand,2,"r",0,NULL,1,1,1,0,0},
{"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
{"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0},
// 略
};
因为数组的查询效率较低,所以 redis 在服务启动前,将所有的 command 转为一个 dict 来存储,方便后续的查询(这个也符合 table driven 编程的方法):
// 根据 commandTable 构造 server.commands 查询 dict
void populateCommandTable(void) {
int j;
// 命令的数量
int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
for (j = 0; j < numcommands; j++) {
// 指定命令
struct redisCommand *c = redisCommandTable+j;
// 取出字符串 FLAG
char *f = c->sflags;
int retval1, retval2;
// 解析 sflags,计算 flags
while(*f != '