Redis系列(一)---啟動流程分析


我們知道,Redis是一個性能非常優異的kv服務器,有關redis的性能及適用場景,在后期做介紹,這里重點介紹下redis的啟動流程,也是對近期對redis代碼閱讀的一點總結,有不足之處,歡迎拍磚.

閱讀c/c++項目的源碼,一般情況下,都將從main函數,那么對於redis的啟動流程,下邊也從main函數開始說起.

首先,main函數里邊聲明了一個time_t start 變量, 用來對一些操作進行時間統計,如從AOF文件中加載數據,從redisdb中加載數據。

接下來,調用initServerConfig() 對struct redisServer server 這一個全部變量進行默認初始化.(如果啟動redis時指定了redis.conf,后邊會用配置文件中的配置覆蓋這里初始化的值)。 然后是對命令行參數的讀取,參數個數不能超過3個, 可以以test-memory指定內存數運行redis;

如果啟動參數指定了redis.conf, 那么首先調用resetServerSaveParams() 重置server.saveparams(釋放該指針指向的內存單元並設置為NULL, 同時將server.saveparamslen置為0):

 

 

void resetServerSaveParams() {
    zfree(server.saveparams);
    server.saveparams = NULL;
    server.saveparamslen = 0;
}

 

然后調用loadServerConfig(char *filename) 對 server 全局變量重新初始化。(具體實現方式暫時不做具體分析)

判斷 server.daemonize,已決定是否一daemon方式啟動redis.

if (server.daemonize) daemonize();

接下來調用 initServer(), 初始化服務器,初略來說,這個函數完成的工作有:

調用 createSharedObjects() 初始化 全局的shared對象

調用 aeCreateEventLoop() 創建事件輪詢:

 

server.el = aeCreateEventLoop();
      aeCreateEventLoop完成的工作:

      1:為aeEventLoop分配內存: eventLoop = zmalloc(sizeof(*eventLoop));

      2:調用aeApiCreate(aeEventLoop) 對eventLoop進行初始化,在這個函數里邊, 首先調用zmalloc為aeApiState 分配內存, 然后調用epoll_create創建一個fd, 並將其賦值給 aeApiState的epfd, 然后整個aeApiState 賦值給eventLoop的apidata.

typedef struct aeApiState {
    int epfd;
    struct epoll_event events[AE_SETSIZE];
} aeApiState;

static int aeApiCreate(aeEventLoop *eventLoop) {
    aeApiState *state = zmalloc(sizeof(aeApiState));

    if (!state) return -1;
    state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */
    if (state->epfd == -1) return -1;
    eventLoop->apidata = state;
    return 0;
}

aeEventLoop *aeCreateEventLoop(void) {
    aeEventLoop *eventLoop;
    int i;

    eventLoop = zmalloc(sizeof(*eventLoop));
    if (!eventLoop) return NULL;
    eventLoop->timeEventHead = NULL;
    eventLoop->timeEventNextId = 0;
    eventLoop->stop = 0;
    eventLoop->maxfd = -1;
    eventLoop->beforesleep = NULL;
    if (aeApiCreate(eventLoop) == -1) {
        zfree(eventLoop);
        return NULL;
    }
    /* Events with mask == AE_NONE are not set. So let's initialize the
     * vector with it. */
    for (i = 0; i < AE_SETSIZE; i++)
        eventLoop->events[i].mask = AE_NONE;
    return eventLoop;
}

 

然后調用anetTcpServer和anetUnixServer 創建對端口和unix域套接字的監聽, 並將返回值賦值給 server.ipfd和server.sofd。通知給這兩個套接字設置AE_READABLE事件,並設置相應的處理函數

  

if (server.port != 0) {
        server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr);
        if (server.ipfd == ANET_ERR) {
            redisLog(REDIS_WARNING, "Opening port %d: %s",
                server.port, server.neterr);
            exit(1);
        }
    }
    if (server.unixsocket != NULL) {
        unlink(server.unixsocket); /* don't care if this fails */
        server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm);
        if (server.sofd == ANET_ERR) {
            redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr);
            exit(1);
        }
    }
 if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
        acceptTcpHandler,NULL) == AE_ERR) oom("creating file event");
    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
        acceptUnixHandler,NULL) == AE_ERR) oom("creating file event");

 

判斷是否需要打開aof文件。

 

 if (server.appendonly) {
        server.appendfd = open(server.appendfilename,O_WRONLY|O_APPEND|O_CREAT,0644);
        if (server.appendfd == -1) {
            redisLog(REDIS_WARNING, "Can't open the append-only file: %s",
                strerror(errno));
            exit(1);
        }
    }

 

initServer 函數返回后, 根據server.appendonly的值判斷是否需要從aof文件或者rdb裝載數據。

 

 start = time(NULL);
    if (server.appendonly) {
        if (loadAppendOnlyFile(server.appendfilename) == REDIS_OK)
            redisLog(REDIS_NOTICE,"DB loaded from append only file: %ld seconds",time(NULL)-start);
    } else {
        if (rdbLoad(server.dbfilename) == REDIS_OK) {
            redisLog(REDIS_NOTICE,"DB loaded from disk: %ld seconds",
                time(NULL)-start);
        } else if (errno != ENOENT) {
            redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting.");
            exit(1);
        }
    }

 

然后就設置每次進入事件處理函數之前需要執行的函數

aeSetBeforeSleepProc(server.el,beforeSleep);

然后調用aeMain(server.el);開始事件循環。

 

aeMain(server.el);

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}

如果事件輪詢結束(acMain返回),則調用aeDeleteEventLoop(server.el);刪除eventLoop

 

void aeDeleteEventLoop(aeEventLoop *eventLoop) {
    aeApiFree(eventLoop);
    zfree(eventLoop);
}

至此 main函數結束,服務器退出。

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM