Redis系列文章
原理篇
源码篇
- 【Redis源码分析之基础流程】
- 【Redis源码分析之持久化】
- 【Redis源码分析之主从复制】
- 【Redis源码分析之哨兵】
- 【Redis源码分析之集群故障转移】
- 【Redis源码分析之集群Meet命令和请求路由】
问题分析
Redis源码分析之基础流程
本文简要分析一下Redis源码的基本流程,为后续Redis源码分析做好基础。
1. main函数流程
main函数是程序的入口,会做配置去读,服务实例初始化,事件循环等操作。
//server.c#main
int main(int argc, char **argv) {
...
initServerConfig();
...
loadServerConfig(configfile,options);
...
initServer();
...
aeSetBeforeSleepProc(server.el,beforeSleep);
aeSetAfterSleepProc(server.el,afterSleep);
aeMain(server.el);
aeDeleteEventLoop(server.el);
}
initServer主要做了一些server结构体的初始化;另外还会监听端口;创建数据库;把serverCron函数加入到事件循环中;为监听的端口注册事件。
//server.c#initServer
void initServer(void) {
...
listenToPort(server.port,server.ipfd,&server.ipfd_count)
...
aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL)
...
aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,acceptTcpHandler,NULL)
}
2. 命令执行流程
在main函数中,会对监听端口进行事件监听,处理函数为:acceptTcpHandler。在该方法内部,会accept客户端的连接。然后调用acceptCommonHandler方法
//networking.c#acceptTcpHandler
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
int cport, cfd, max = MAX_ACCEPTS_PER_CALL;
while(max--) {
cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
acceptCommonHandler(cfd,0,cip);
}
}
在acceptCommonHandler方法中,会调用createClient方法,进行创建client结构体的数据。
//network.c#acceptCommonHandler
static void acceptCommonHandler(int fd, int flags, char *ip) {
createClient(fd);
}
在createClient方法中,除了client结构体的创建之外。还会对这个客户端连接进行绑定处理函数,为readQueryFromClient。
//network.c#createClient
client *createClient(int fd) {
aeCreateFileEvent(server.el,fd,AE_READABLE,readQueryFromClient, c)
}
在该方法中,会读取数据;然后调用processInputBufferAndReplicate方法进行处理。
//network.c#readQueryFromClient
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
nread = read(fd, c->querybuf+qblen, readlen);
...
processInputBufferAndReplicate(c);
}
在该方法中,最主要是调用processInputBuffer方法。
//network.c#processInputBufferAndReplicate
void processInputBufferAndReplicate(client *c) {
if (!(c->flags & CLIENT_MASTER)) {
processInputBuffer(c);
} else {
size_t prev_offset = c->reploff;
processInputBuffer(c);
size_t applied = c->reploff - prev_offset;
if (applied) {
replicationFeedSlavesFromMasterStream(server.slaves,
c->pending_querybuf, applied);
sdsrange(c->pending_querybuf,applied,-1);
}
}
}
在该方法中,最重要的是,会调用processCommand方法。
//network.c#processInputBuffer
void processInputBuffer(client *c) {
...
if (processCommand(c) == C_OK)
...
}
在processCommand方法中会以及通过参数,将本次的命令获取到(redisCommand结构体),然后有一系列的判断(后续遇到再说)。然后最重要的是会调用call方法。
//server.c#processCommand
int processCommand(client *c) {
...
c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
...
call(c,CMD_CALL_FULL);
...
}
在执行call方法中,最主要是调用命令对应方法。同时也会记录一下状态,如慢日志,AOF日志(如果有)之类的。
//server.c#call
void call(client *c, int flags) {
...
c->cmd->proc(c);
...
}
3. 事件循环
在main函数中,会在事件循环结构aeEventLoop上,添加beforeSleep、afterSleep的处理函数。主线程中调用aeMain方法。
//server.c#main
aeSetBeforeSleepProc(server.el,beforeSleep);
aeSetAfterSleepProc(server.el,afterSleep);
aeMain(server.el);
aeMain函数内部是一个死循环,处理beforesleep函数和aeProcessEvents
//ae.c#aeMain
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
}
}
在aeProcessEvents方法中,会读取事件,然后会执行aftersleep函数。然后遍历事件数,得到连接描述符,执行相应的处理函数。
//ae.c#aeProcessEvents
int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
...
numevents = aeApiPoll(eventLoop, tvp);
/* After sleep callback. */
if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)
eventLoop->aftersleep(eventLoop);
for (j = 0; j < numevents; j++) {
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
int fd = eventLoop->fired[j].fd;
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
//或者执行下面的方法,排版问题,去掉了条件判断
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
}
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop);
}
在对连接描述符添加处理函数的方法如下,aeCreateFileEvent。会设置fe->rfileProc或者fe->wfileProc的处理函数。
//ae.c#aeCreateFileEvent
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
{
if (fd >= eventLoop->setsize) {
errno = ERANGE;
return AE_ERR;
}
aeFileEvent *fe = &eventLoop->events[fd];
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
return AE_ERR;
fe->mask |= mask;
if (mask & AE_READABLE) fe->rfileProc = proc;
if (mask & AE_WRITABLE) fe->wfileProc = proc;
fe->clientData = clientData;
if (fd > eventLoop->maxfd)
eventLoop->maxfd = fd;
return AE_OK;
}
在processTimeEvents方法中,会根据一些条件判断,执行timeProc对应的处理函数。(如果是Redis实例,其中会有serverCron的处理函数)
//ae.c#processTimeEvents
static int processTimeEvents(aeEventLoop *eventLoop) {
...
retval = te->timeProc(eventLoop, id, te->clientData);
...
}
4. 参考资料
-
《Redis 5设计与源码分析》
-
Redis 5.0.12 源码