Valkey源码剖析(13):执行命令¶
在将命令请求的参数以及命令结构本身记录到客户端对应的client结构之后,服务器就会调用server.c/processCommand()函数,进行执行命令之前的检查:
int processCommand(client *c) {
// ...
}
这些检查包括:
检查命令及命令参数是否存在,格式是否正确,排除可能出现的隐性错误
验证客户端的身份和权限能否执行被请求的命令
拒绝执行某些被保护的命令
如果服务器运行在集群模式下,那么考虑是否需要对命令进行转向
处理可能出现的失效情况,尽量避免执行命令可能带来的数据不一致
检查服务器已连接客户端的内存占用情况,并在有需要的情况下释放部分客户端,以便为接下来将要执行的命令留出足够的内存
执行
maxmemory指令检查持久化功能是否正常,尽量确保写命令的正确性
如果服务器为主节点,那么检查主从连接的健康程度以及从节点的数量,尽量确保写命令不会导致主从不一致
检查服务器是否正在载入数据库,如果是的话只允许带有
CMD_LOADING标识的命令执行如果服务器正在处理繁重的工作(比如在执行脚本或调用模块的时候),只允许执行部分命令
如果服务器正处于阻塞状态,那么阻塞该命令直到阻塞解除
等等。
在通过前面大量检查之后,processCommand()函数将调用server.c/call()函数,正式开始执行命令:
int processCommand(client *c) {
// 执行命令之前的大量检查和测试……
if (c->flag.multi && c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
c->cmd->proc != quitCommand &&
c->cmd->proc != resetCommand) {
// 处理事务情况
// ...
} else {
int flags = CMD_CALL_FULL;
// 实际地执行命令
call(c, flags);
if (listLength(server.ready_keys) && !isInsideYieldingLongCommand()) handleClientsBlockedOnKeys();
}
return C_OK;
}
call()函数负责设置服务器和客户端的状态以及各项统计数据,为命令的执行做好最后的准备工作,然后以reactor模式调用客户端请求的命令实现函数,从而实现命令的实际执行:
void call(client *c, int flags) {
// ...
// 调用命令实现函数以执行命令
c->cmd->proc(c);
// ...
}
在命令函数执行之后,call()函数会再次更新各项状态信息和统计数据、日志等等,并做一些善后处理工作(比如资源回收等)。
以上就是Valkey服务器执行客户端命令的具体流程了。
黄健宏
2026.1.6