zoukankan      html  css  js  c++  java
  • 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函数结束,服务器退出。

     

     

     

     

     

     

     

     

  • 相关阅读:
    115. Distinct Subsequences
    91. Decode Ways
    72. Edit Distance
    python 输出 a+b
    求次小生成树
    判断最小生成树是否唯一
    二分法求最优值
    黑科技
    线段树+ 区间更新
    2018ICPC青岛赛区J题
  • 原文地址:https://www.cnblogs.com/yuxingfirst/p/2774438.html
Copyright © 2011-2022 走看看