Redis中的执行命令的过程
在redis.c的initServerConfig()方法中,通过调用dictCreate方法初始化server端的命令表。这个命令表是一个hashtable,可以通过key找到相关的命令:
/* Command table -- we initiialize it here as it is part of the * initial configuration, since command names may be changed via * redis.conf using the rename-command directive. */ server.commands = dictCreate(&commandTableDictType, NULL); server.orig_commands = dictCreate(&commandTableDictType, NULL); populateCommandTable(); server.delCommand = lookupCommandByCString("del"); server.multiCommand = lookupCommandByCString("multi"); server.lpushCommand = lookupCommandByCString("lpush"); server.lpopCommand = lookupCommandByCString("lpop"); server.rpopCommand = lookupCommandByCString("rpop"); |
initServer()函数在初始化服务端的基本配置时,已经提前创建了客户端的回调函数。具体的调用为:
1.创建客户端的文件读事件的回调:acceptTcpHandler/acceptUnixHandler
2.一旦接收到客户端连接请求后,调用acceptCommonHandler()方法。在接收到客户端请求后,可以得到客户端的fd(file description),这个fd在调用acceptCommonHandler时会传入。
3.acceptCommonHandler内部根据fd调用createClient创建redis客户端的上下文信息。
4.createClient内部又创建了一个事件回调函数readQueryFromClient,用于读取客户端的传入的信息。
5.readQueryFromClient从客户端读取实际的请求内容,然后会做一个初步判断客户端的请求内容是否合理。不合理的请求在这里直接被拦截处理了。这里判断请求是否合理主要是判断网络层面的内容是否合理。如果网络层相关的请求合理的话,调用processInputBuffer方法解析客户端的请求类型。
6.processInputBuffer会解析客户端的reqtype是inline还是multibulk的。如果是inline的,调用processInlineBuffer函数;否则调用processMultibulkBuffer。这两个函数在解析完客户端发送的报文指令后,将解析出的内容放入redisClient结构的argv/argc字段中。如果argc没有,直接重置客户端,关闭客户端的连接;否则调用processCommand方法处理客户端的命令。
7.processCommand首先根据argv的内容,然后调用lookupCommand()方法从server.commands中找到需要执行的命令,然后将找到的命令赋值到client的cmd和lastcmd字段上。
c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
8.找到相关的命令后,调用call方法,执行客户端的命令。执行命令很简单,因为上一步中已经找到了客户端的cmd了。每个命令都有一个与cmd像对应的proc()方法,proc()方法接受一个redisclient的参数。call方法内部调用c->cmd->proc()方法时,将当前redisClient的变量c传入,即实现了根据客户端的不同指令来进行相应处理的操作了。
9.至此,redis执行客户端命令的过程就算结束了。