Redis源码分析--服务器(1)结构与启动

服务器:

一、服务器结构体 redisServer:

struct redisServer {
    char *configfile;
    int hz;

    int dbnum;
    redisDb *db;
    dict *commands;
 
    aeEventLoop *el;
 
    int port;                   
    char *bindaddr[CONFIG_BINDADDR_MAX];
    int bindaddr_count;
    
	int ipfd[REDIS_BINDADDR_MAX]; /* TCP socket file descriptors */
    int ipfd_count;             /* Used slots in ipfd[] */
 
    list *clients; 
    int maxidletime;   
    
      /* Pubsub */
    dict *pubsub_channels;  /* Map channels to list of subscribed clients */
    list *pubsub_patterns;  /* A list of pubsub_patterns */
    int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an
                                   xor of REDIS_NOTIFY... flags. */
}
  • L15、L16:redis可以配置在多个网卡上,所以配置多个socket fd;

    在config.c中可以看到,服务器启动加载配置文件时,如果配置文件中有bind项,才会有多个ipfd;

    if (!strcasecmp(argv[0],"bind") && argc >= 2) {
        int j, addresses = argc-1;
    
        if (addresses > REDIS_BINDADDR_MAX) {
            err = "Too many bind addresses specified"; goto loaderr;
        }
        for (j = 0; j < addresses; j++)
            server.bindaddr[j] = zstrdup(argv[j+1]);
        server.bindaddr_count = addresses;
    }
    
  • L6:服务器中的数据库:

    typedef struct redisDb {
        dict *dict;                 /* The keyspace for this DB */
        dict *expires;              /* Timeout of keys with a timeout set */
        int id;
        long long avg_ttl;          /* Average TTL, just for stats */
    } redisDb;
    
    • L2: 这个字典称作 键空间 key space,就是用户所见到的数据库;

      所有对数据库的操作(添加或删除数据库),都是对这个键空间字典的操作;

    • L2:键的过期字典;


二、服务器的启动与初始化:

1、启动服务器:

​ Redis服务器启动后,首先执行main函数:

int main(int argc, char **argv) {
    // ...
	initServer();
    // ...
    // 解析参数,加载配置文件等
    // ...
    aeMain(server.el);
    aeDeleteEventLoop(server.el);
    return 0;
}
  • 服务器首先进行初始化 L3,接着就进入事件循环[2] L7

2、初始化服务器:

​ 接着看服务器初始化需要哪些步骤:

void initServer() {
    // ...
    /* 创建事件循环和数据库 */
    server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);
    server.db = zmalloc(sizeof(redisDb)*server.dbnum);
    
    // ...
    
    /* Create the serverCron() time event, that's our main way to process
     * background operations. */
    if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
        redisPanic("Can't create the serverCron time event.");
        exit(1);
    }
    
     /* Create the Redis databases, and initialize other internal state. */
    for (j = 0; j < server.dbnum; j++) {
        server.db[j].dict = dictCreate(&dbDictType,NULL);
        server.db[j].expires = dictCreate(&keyptrDictType,NULL);
        server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].ready_keys = dictCreate(&setDictType,NULL);
        server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].id = j;
        server.db[j].avg_ttl = 0;
    }
    // ...
    /* 为每个bind的端口开一个socket用来处理连接 */
    for (j = 0; j < server.ipfd_count; j++) {
        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
            acceptTcpHandler,NULL) == AE_ERR)
            {
                redisPanic(
                    "Unrecoverable error creating server.ipfd file event.");
            }
    }
    // ...
}
  • L3:在事件循环中加入对时间事件serverCron的监听 //todo;

  • L29:此时绑定了port和地址的socket fd正在listen;创建监听可读事件,回调acceptTcpHandler accept客户端连接,创建客户端;

    void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
        int cport, cfd;
        char cip[REDIS_IP_STR_LEN];
        REDIS_NOTUSED(el);
        REDIS_NOTUSED(mask);
        REDIS_NOTUSED(privdata);
        /* 进行accept */
        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
        if (cfd == AE_ERR) {
            redisLog(REDIS_WARNING,"Accepting client connection: %s", server.neterr);
            return;
        }
        redisLog(REDIS_VERBOSE,"Accepted %s:%d", cip, cport);
        /* 会调用createClient创建客户端 */
        acceptCommonHandler(cfd,0);
    }
    
    • L4~L6:学习一下,避免编译器报Warning:

      /* Anti-warning macro... */
      #define REDIS_NOTUSED(V) ((void) V)
      

参考:

  1. 【Redis源码分析】Redis命令处理生命周期 - SegmentFault 思否

  2. Redis源码分析--事件处理器 - macguz - 博客园 (cnblogs.com)

posted @ 2022-02-07 17:17  macguz  阅读(79)  评论(0编辑  收藏  举报