如下:
void initSetver(void) { // 处理 SIG_XXX 信号 // ... // 初始化server.xxx下的一部分字段, 值得关注的有: server.pid = getpid() server.current_client = NULL; server.clients = listCreate(); server.clients_to_close = listCreate(); //... server.clients_pending_write = listCreate(); //... server.unblocked_clients = listCreate(); server.ready_keys = listCreate(); server.clients_waiting_acks = listCreate(); //... server.clients_paused = 0 //... // 创建全局共享对象 createSharedObjects(); // 尝试根据最大支持的客户端连接数, 调整最大打开文件数 adjustOpenFilesLimit(void) // 创建事件处理器 server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR) if (server.el == NULL) { // ... exit(1); } // 初始化数据库数组 server.db = zmalloc(sizeof(redisDb) * server.dbnum); // 监听tcp端口, tcp 监听文件描述符存储在 server.ipfd 中 if (server.port != 0 && listenToPort(server.port, server.ipfd, &server.ipfd_count) == C_ERR) exit(1); // 监听unix端口 if (server.unixsocket != NULL) { //... } // 如果没有任何tcp监听fd存在, abort if (server.ipfd_count == 0 && server.sofd < 0) { //... exit(1); } // 初始化数据库数组中的各个数据库 for (j = 0; j < server.dbnum; j++) { server.db[j].dict = dictCreate(&dbDictType, NULL); // 初始化键空间 server.db[j].expires = dictCreate(&keyptrDictType, NULL); // 初始化有时效的键字典. key == 键, value == 过期时间戳 server.db[j].blocking_keys = dictCreate(&keylistDictType, NULL);// 初始化阻塞键字典. server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType, NULL); // 初始化解除阻塞键字典 server.db[j].watched_keys = dictCreate(&keylistDictType, NULL); // 初始化与事务相关的一个字典 server.db[j].id = j; // 初始化数据库的编号 server.db[j].avg_ttl = 0; // 初始化统计数据 } // 其它一大堆细节操作 // ... // 向事件处理器中加一个定时回调, 这个回调中处理了很多杂事. 比如清理过期客户端连接, 过期key等 if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) { //... exit(1); } // 将tcp与unix监听fd添加到事件处理器中 // 回调函数分别为: acceptTcpHandler和acceptUnixHandler 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"); } } if (server.sofd > 0 && aeCreateFileEvent(server.el, server.sofd, AE_READABLE, acceptUnixHandler, NULL) == AE_ERR) { serverPanic("Unrecoverable error creating server.sofd file event."); } // 注册一个额外的事件回调: 用于唤醒一个被module操作阻塞的客户端 // ... // 如有需要, 打开AOF文件 // ... // 对32位机器, 且没有配置maxmemory时, 自动设置为3GB // ... // 其它与单机数据库无关的操作 // ... }以上就是单机数据库的启动流程
总结:
Redis服务端进程中, 以server这个全局变量来描述服务端
server全局变量中, 持有着所有数据库的指针(server.db), 一个事件处理器(server.el), 与所有与其连接的客户端(server.clients), 监听的tcp/unix socket fd(server.ipfd, server.sofd)
在Redis服务端启动过程中, 会初始化监听的tcp/unix socket fd, 并把它们加入到事件处理器中, 相应的事件回调分别为acceptTcpHandler与acceptUnixHandler
5. 客户端与Redis服务端建立连接的过程上面已经讲了Redis单机数据库服务端的启动过程, 下面来看一看, 当一个客户端通过tcp连接至服务端时, 服务端会做些什么. 显然要从listening tcp socket fd在server.el中的事件回调开始看起.
逻辑上来讲, Redis服务端需要做以下几件事:
建立连接, 接收请求
协议交互, 命令处理