3.3 命令执行:processCommand
实现在server.c,实际执行命令前的主要逻辑:
- processCommand调用moduleCallCommandFilters,将Redis命令替换成module想替换的命令
- processCommand判断当前命令是否为quit命令并做相应处理
3.processCommand调用lookupCommand,在全局变量server的commands成员变量中查找相关命令
全局变量server的commands成员变量是个哈希表,定义在redisServer结构体:
commands成员变量的初始化是在initServerConfig,调用dictCreate完成哈希表创建,再调用populateCommandTable将Redis提供的命令名称和对应的实现函数,插入哈希表。
而这其中的populateCommandTable使用了redisCommand结构体数组redisCommandTable。
redisCommandTable数组是在server.c文件中定义的,它的每一个元素是一个redisCommand结构体类型的记录,对应了Redis实现的一条命令。也就是说,redisCommand结构体中就记录了当前命令所对应的实现函数是什么。
如下代码展示GET、SET等命令信息,实现函数分别是getCommand,setCommand:
所以lookupCommand会根据解析的命令名称,在commands对应的哈希表中查找相应命令。
那么,一旦查到对应命令后,processCommand函数就会进行多种检查,比如命令的参数是否有效、发送命令的用户是否进行过验证、当前内存的使用情况,等等。这部分的处理逻辑比较多,你可以进一步阅读processCommand函数来了解下。
这样,等到processCommand对命令做完各种检查后,就开始执行命令,会判断当前客户端是否有CLIENT_MULTI标记:
- 若有,说明要处理Redis事务相关命令
就要按事务要求,调用queueMultiCommand:将命令入队保存,等待后续再一把梭处理。
- 若无,无关事务特性
processCommand调用call:实际执行命令。call函数执行命令是通过调用命令本身,即redisCommand结构体中定义的函数指针完成。每个redisCommand结构体中都定义了其对应实现函数,在redisCommandTable数组可查到。
分布式锁的加锁操作就是使用SET命令实现的,所以来看SET命令为例,来看一个命令实际执行过程。
SET命令对应实现函数setCommand:首先会判断命令参数,如是否带有NX、EX、XX、PX等可选项,若有,就会记录这些标记。
然后,setCommand会调用setGenericCommand:根据setCommand记录的命令参数标记,进行相应处理。如命令参数中有NX,则setGenericCommand会调用lookupKeyWrite,查找要执行SET命令的key是否已存在。
若K已存在,则setGenericCommand会调用addReply,返回NULL,正符合分布式锁的语义。
若SET命令可正常执行,即:
- 命令带NX选项但K并不存在
- 或带有XX选项但K已存在
这样setGenericCommand就会调用setKey完成KV对的实际插入:
setKey(c->db,key,val);
然后,若命令设置了TTL,setGenericCommand还会调用setExpire函数设置过期时间。最后,setGenericCommand函数会调用addReply函数,将结果返回给客户端,如下所示:
addReply(c, ok_reply ? ok_reply : shared.ok);
SET命令执行流程:
无论:
- 在命令执行过程中,发现不符合命令的执行条件
- 或是命令能成功执行
addReply函数都会被调用以返回结果。所以,这就进入命令处理过程的最后一个阶段:结果返回阶段。