前面讲过,ae循环在收到客户端请求时,会调用请求处理器——acceptTcpHandler ,而请求处理器会创建新的套接字并监听和绑定命令处理器——readQueryFromClient。本篇着重分析命令的执行过程。
大概可分为:
1、读取并分析套接口中协议格式的命令请求,设置redisClient的queryBuf、argv和argc属性------(processInlineBuffer)
2、执行命令------(processCommand)
- 查找对应的命令实现
- 执行预备操作
- 执行命令及执行完的后续操作
3、将命令回复发送给客户端------(sendReplyToClient)
源码分析:
首先一路跟踪readQueryFromClient函数来到processInputBuffer。这里主要看processInlineBuffer和processCommand分别对应上面的1、2两点
1、读取并分析套接口中协议格式的命令请求,设置redisClient的argv和argc属性(processInlineBuffer)
2、执行命令(processCommand)
上面除了lookupCommand(匹配命令实现函数)外,其他的大都是命令执行前的预备操作,而真正执行命令的地方在call方法
call:
其中c->cmd->proc©这一行即执行了对应的命令实现函数。后面的则是执行命令的后续操作(但不包括返回给客户端的操作)
3、将命令回复发送给客户端
跟踪完readQueryFromClient会发现并没有看到有回复执行结果给客户端的代码。
那么redis是如何将执行结果回复给客户端的呢?
以set命令为例,点开setCommand可以看到addReply方法
不点进去看的同学可能会以为这里就是回复了。但其实不然,这里只是将当前客户端加入了server.clients_pending_write,并且将执行结果存到bufpos缓存区而已,并没有真正的回复。。。
代码看到这里,一次事件处理就已经看完了。
但是还是没有回复客户端啊???究竟是如何回复的?
答案在aeMain(redis事件处理循环)
前面讲过,除了将执行结果存到缓存区外,还会将当前客户端加入server.clients_pending_write。而在aeMain每次处理事件之前会先执行beforesleep方法
而beforeSleep会执行handleClientsWithPendingWrites(处理缓存区里的写数据)
handleClientsWithPendingWrites方法会先尝试执行writeToClient直接回复。若不行,再执行aeCreateFileEvent创建一个文件写事件并绑定回复处理器sendReplyToClient加入事件循环,由后面的事件循环再调用。
最后来一波命令回复的总结:
参考文献:《Redis设计与实现》黄健宏著、redis-5.0.14源码