Redis v6.2.0 server接收client命令
以standalone模式按默认配置启动redis-server,只考虑最基本的情况
1. 准备连接
struct redisServer {
int ipfd[CONFIG_BINDADDR_MAX]; /* TCP socket file descriptors */
int ipfd_count; /* Used slots in ipfd[] */
}
默认ipfd_count为2,一个监听本地TCP4,另一个监听本地TCP6。
Redis启动时,通过
int listenToPort(int port, int *fds, int *count)
先创建socket,绑定端口并监听客户端请求。即此时完成了bind()和listen(),但还没有开始accept()
for (j = 0; j < server.ipfd_count; j++) {
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR)
{
serverPanic(
"Unrecoverable error creating server.ipfd file event.");
}
}
之后的main函数将调用aeCreateFileEvent()为本地监听的socket创建一个可读的文件事件,并将socket加入到server的epoll对象中,并以acceptTcpHandler作为监听socket的回调函数。
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
aeProcessEvents(eventLoop, AE_ALL_EVENTS|
AE_CALL_BEFORE_SLEEP|
AE_CALL_AFTER_SLEEP);
}
}
所有的启动准备工作(启动监听、加载RDB等)结束后,main函数调用aeMain()循环处理所有的文件事件和时间事件
numevents = aeApiPoll(eventLoop, tvp);
for (j = 0; j < numevents; j++) {
/* Fire the readable event if the call sequence is not
* inverted. */
if (!invert && fe->mask & mask & AE_READABLE) {
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
fired++;
fe = &eventLoop->events[fd]; /* Refresh in case of resize. */
}
}
在aeProcessEvents()中,aeApiPoll()会调用epoll_wait()获取当前已经就绪的事件,然后for循环依次处理,文件可读事件就会调用rfileProc回调函数。
假设这时有客户端发起连接,则for循环中rfileProc实际上就调用了之前注册的acceptTcpHandler函数用于处理连接请求。
2. 准备读取
在acceptTcpHandler中相当于是执行了listen之后的一次accept(),此时已经和client建立了连接但还没有读到client准备执行的命令。这时的情况就类似之前执行了listen()但没有accept()。实际上处理方式也和之前类似,即再为当前的连接设置一个文件读事件,将连接的socket加入到server的epoll对象中,回调函数是readQueryFromClient(),当有客户端命令发来时,readQueryFromClient()就可以读到客户端命令并调用相关的执行函数了。