Redis server启动后会做哪些操作?

Redis 是典型的 Client-Server 架构,一旦 Redis 实例开始运行,Redis server 也就会启动,而 main 函数其实也会负责 Redis server 的启动运行。

Redis 针对以下三个问题的实现思路:

Redis server 启动后具体会做哪些初始化操作?
Redis server 初始化时有哪些要害配置项?
Redis server 如何开始解决客户端申请?

(1) Redis Server 启动

源码地址 https://github.com/redis/redi...

//file: src/server.c// #L5297  5297行int main(int argc, char **argv) {  // 省略局部代码...   // 1. 启动RedisServer时 初始化配置及资源  initServer();  // 2. 循环解决申请及事件,直到服务器敞开为止  aeMain(server.el); }

(1.1) 启动RedisServer初始化配置及资源

在 initServer 这个函数内,Redis 做了这么三件重要的事件。

1、创立一个epoll对象
2、对配置的监听端口进行listen
3、把 listen socket 让 epoll 给治理起来

void initServer(void) {    // 1. 创立epoll    server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);    // 2. 监听端口    listenToPort(server.port,server.ipfd,&server.ipfd_count)    // 3. 注册accept事件处理器 (只是注册 前面会用到)    for (j = 0; j < server.ipfd_count; j++) {        aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,            acceptTcpHandler,NULL)    }}

(1.2) 循环解决申请

// file: src/ae.c /** * 循环处理事件 *  * @param *eventLoop  */ void aeMain(aeEventLoop *eventLoop) {    eventLoop->stop = 0;    // 循环处理事件    while (!eventLoop->stop) {        // 处理事件        aeProcessEvents(eventLoop, AE_ALL_EVENTS|                                   AE_CALL_BEFORE_SLEEP|                                   AE_CALL_AFTER_SLEEP);    }}
// file: src/ae.c /** * 处理事件 *  * @param *eventLoop * @param flags */ int aeProcessEvents(aeEventLoop *eventLoop, int flags){    // 省略局部细节...    // 调用多路复用API获取就绪事件    numevents = aeApiPoll(eventLoop, tvp);    // 解决写事件    fe->wfileProc(eventLoop,fd,fe->clientData,mask);    // 解决读事件    fe->rfileProc(eventLoop,fd,fe->clientData,mask);                        }

其实整个 Redis 的工作过程,就只须要了解分明 main 函数中调用的 initServeraeMain 这两个函数就足够了。


(2) RedisServer网络申请解决流程

(2.1) 绑定地址并监听套接字(bind listen)

// file:  src/server.c // 监听端口/** * @param port * @param *fds * @param *count */int listenToPort(int port, int *fds, int *count) {    // ...     for (j = 0; j < server.bindaddr_count || j == 0; j++) {        // ...  省略 绑定IPV6 IPV4的细节  anetTcp6Server  anetTcpServer        // 绑定        fds[*count] = anetTcpServer(server.neterr,port,NULL,                    server.tcp_backlog);        // ...      }    return C_OK;}

Redis 是反对开启多个端口的,所以在 listenToPort 中咱们看到是启用一个循环来调用 anetTcpServer。
在 anetTcpServer 中,逐渐会开展调用,直到执行到 bind 和 listen 零碎调用。

// file:  src/anet.c /** * @param *err * @param port * @param *bindaddr * @param backlog */int anetTcpServer(char *err, int port, char *bindaddr, int backlog){    return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);}/** * @param *err * @param port * @param *bindaddr * @param af * @param backlog */static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog){    int s = -1, rv;    char _port[6];  /* strlen("65535") */    struct addrinfo hints, *servinfo, *p;    // 创立套接字    s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)    // 设置端口重用    anetSetReuseAddr(err,s)    // 监听    anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog)
/** * 监听 *  * @param *err * @param s 对应创立的套接字fd * @param *sa socket地址信息 (协定 地址) * @param len * @param backlog  */static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {    // 绑定ip端口    bind(s,sa,len)    // 监听套接字    listen(s, backlog)    return ANET_OK;}


(2.2) 和客户端建设连贯(accept)

// file: src/networking.c /** * 接管tcp处理器 *  * @param *el * @param fd * @param *privdata * @param mask */void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {        // ...         // 接管tcp申请        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);        // ...         // 接管通用解决        acceptCommonHandler(connCreateAcceptedSocket(cfd),0,cip);}

(2.2.1) 接管tcp申请-anetTcpAccept

// file: src/anet.c /** * 接管tcp申请  *  * @param *err * @param s fd * @param *ip * @param ip_len * @param *port  */int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) {    int fd;    struct sockaddr_storage sa; // 套接字地址存储构造体     socklen_t salen = sizeof(sa);    // 接管申请    fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)            // ...     return fd;}
// file: src/anet.c /** * @param *err * @param s * @param *sa * @param *len */ static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) {    int fd;    while(1) {        // 接管socket数据        // fd是socket返回的socket,指向的定义的SOCKADDR_IN 构造体指针,指针的大小        fd = accept(s,sa,len);        // ...        break;    }    return fd;}

参考资料

Redis高性能IO模型
https://weikeqin.com/2022/01/...

Redis源码分析与实战 学习笔记 Day8 08 | Redis server启动后会做哪些操作?
https://time.geekbang.org/col...