Redis的主要脉络梳理

来源:互联网 发布:淘宝上卖牛仔裤的好店 编辑:程序博客网 时间:2024/04/30 20:33

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。


由于工作需要,需要深入研究此开源代码用于选型及设计代码,先从main函数开始吧。

参考版本:Redis-3.2.3


首先从main函数开始@server.c

int main(int argc, char **argv) {// 全局变量 struct redisServer server; /* server global state */ 初始化initServerConfig();    /* Check if we need to start in redis-check-rdb mode. We just execute     * the program main. However the program is part of the Redis executable     * so that we can easily execute an RDB check on loading errors. */    if (strstr(argv[0],"redis-check-rdb") != NULL)        redis_check_rdb_main(argc,argv);        // 重置server.saveparams    resetServerSaveParams();        // 加载redis.conf作为配置文件    loadServerConfig(configfile,options);// 决定是否以daemon方式启动redisserver.supervised = redisIsSupervised(server.supervised_mode);    int background = server.daemonize && !server.supervised;    if (background)     daemonize();// 初始化服务器--主要工作是createSharedObjects();及aeCreateEventLoop创建事件LoopinitServer();// 判断是否需要从aof文件或者rdb装载数据if (!server.sentinel_mode) {loadDataFromDisk();...}else{sentinelIsRunning();}// 设置每次进入事件处理函数之前需要执行的函数aeSetBeforeSleepProc(server.el,beforeSleep);// 进入事件循环主函数,永不退出,除非服务被终止    aeMain(server.el);        // main函数结束、服务器退出    aeDeleteEventLoop(server.el);    return 0;}


下面对子过程进行详细分析:
1、客户端连接处理
initServer()@server.c 这个函数中会调用anetTcpServer和anetUnixServer,分别建立tcp端口和unix域套接字的监听。

anetTcpServer@anet.c
int anetTcpServer(char *err, int port, char *bindaddr, int backlog){    return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);}static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog){// 建立TCP套接字监听s = socket(p->ai_family,p->ai_socktype,p->ai_protocol);anetSetReuseAddr(err,s);anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog);return s;}
以上只是设置要监听的端口、地址、和地址族,再调用anetListen()函数绑定地址并监听端口,这些工作完成后
anetTcpServer 函数返回,并将创建的套接字复制给server.ipfd.

listenToPort(server.port,server.ipfd,&server.ipfd_count)
-->
int listenToPort(int port, int *fds, int *count) {
fds[*count] = anetTcpServer(server.neterr,port,server.bindaddr[j],
                server.tcp_backlog);
}

前面提到有一个EventLoop的事件队列、这个fd将会在此被使用。

void initServer(void) {// 建立主事件队列--EventLoopserver.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);...    /* Create an event handler for accepting new connections in TCP and Unix     * domain sockets. */    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.");            }    }}

首先,从eventLoop的event这个aeFileEvent数组里,取出当前fd对应的acFileEvent,
主要是为了在下边给它设置对应事件的处理函数;即根据传入的mask来判断是哪一类事件。
这里就是 server.ipfd 加入事件监控中、当发生AE_READABLE可读事件时则触发 acceptTcpHandler 函数。

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;}

此时监控建立完毕、此时客户端一个新的连接到来,此时将会触发事件。

主循环事件函数:aeMain@Ae.c

void aeMain(aeEventLoop *eventLoop) {    eventLoop->stop = 0;    // 只要stop标志一直为0则循环永远运行下去    while (!eventLoop->stop) {        if (eventLoop->beforesleep != NULL)            eventLoop->beforesleep(eventLoop);        aeProcessEvents(eventLoop, AE_ALL_EVENTS);    }}/* Process every pending time event, then every pending file event * (that may be registered by time event callbacks just processed). * Without special flags the function sleeps until some file event * fires, or when the next time event occurs (if any). * * If flags is 0, the function does nothing and returns. * if flags has AE_ALL_EVENTS set, all the kind of events are processed. * if flags has AE_FILE_EVENTS set, file events are processed. * if flags has AE_TIME_EVENTS set, time events are processed. * if flags has AE_DONT_WAIT set the function returns ASAP until all * the events that's possible to process without to wait are processed. * * The function returns the number of events processed. */int aeProcessEvents(aeEventLoop *eventLoop, int flags){// 最重要的轮询函数!!!    numevents = aeApiPoll(eventLoop, tvp);    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;        int rfired = 0;    /* note the fe->mask & mask & ... code: maybe an already processed         * event removed an element that fired and we still didn't         * processed, so we check if the event is still valid. */        if (fe->mask & mask & AE_READABLE) {            rfired = 1;            fe->rfileProc(eventLoop,fd,fe->clientData,mask); // 可读回调        }        if (fe->mask & mask & AE_WRITABLE) {            if (!rfired || fe->wfileProc != fe->rfileProc)                fe->wfileProc(eventLoop,fd,fe->clientData,mask); // 可写回调        }        processed++;    }}

客户端新的连接则触发可读事件、即acceptTcpHandler函数

void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {    int cport, cfd, max = MAX_ACCEPTS_PER_CALL; // 当前redis服务器的最大连接总数    char cip[NET_IP_STR_LEN];    while(max--) {        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);        if (cfd == ANET_ERR) {            if (errno != EWOULDBLOCK)                serverLog(LL_WARNING,                    "Accepting client connection: %s", server.neterr);            return;        }        serverLog(LL_VERBOSE,"Accepted %s:%d", cip, cport);                // 接收新的连接后处理函数        acceptCommonHandler(cfd,0,cip);    }}
利用anetTcpAccept产生新的fd并加入到事件队列中

static void acceptCommonHandler(int fd, int flags, char *ip) {    client *c = createClient(fd);// 客户端数目限定    if (listLength(server.clients) > server.maxclients) {        char *err = "-ERR max number of clients reached\r\n";..}}-->client *createClient(int fd) {    client *c = zmalloc(sizeof(client));    /* passing -1 as fd it is possible to create a non connected client.     * This is useful since all the commands needs to be executed     * in the context of a client. When commands are executed in other     * contexts (for instance a Lua script) we need a non connected client. */    if (fd != -1) {        anetNonBlock(NULL,fd);        anetEnableTcpNoDelay(NULL,fd);        if (server.tcpkeepalive)            anetKeepAlive(NULL,fd,server.tcpkeepalive);        // 给前面获取的客户端连接的套接字注册 AE_READABLE事件,并设置事件处理函数readQueryFromClient        if (aeCreateFileEvent(server.el,fd,AE_READABLE,            readQueryFromClient, c) == AE_ERR)        {            close(fd);            zfree(c);            return NULL;        }    }...}

这里结束后则 readQueryFromClient()便是redis处理客户端发送的命令的起始点了。

1 0
原创粉丝点击