• 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)

  • 相关阅读:
    float及清除浮动
    HTML meta标签总结与属性使用介绍
    jQuery相关知识
    FullCalendar日程设置
    Python基础知识
    波段选择
    CSS基础知识
    稀疏表示的高光谱分类
    Sass基本特性
    [Sass]混合宏
  • 原文地址:https://www.cnblogs.com/macguz/p/15868564.html
Copyright © 2020-2023  润新知