Redis Server端处理Client请求的流程图 main函数 main函数主要的功能为:调用initServerConfig函数,进行默认的redisServer数据结构的参数初始化;调用daemonize函数,为服务器开始守护进程,对于守护进行相关详细信息见http://blog.gaodaima.com/acceptedxukai/
Redis Server端处理Client请求的流程图
main函数
main函数主要的功能为:调用initServerConfig函数,进行默认的redisServer数据结构的参数初始化;调用daemonize函数,为服务器开始守护进程,对于守护进行相关详细信息见http://blog.gaodaima.com/acceptedxukai/article/details/8743189;调用initServer函数,初始化服务器;调用loadServerConfig函数,读取Redis的配置文件,使用配置文件中的参数替换默认的参数值;调用aeMain函数,开启事件循环,整个服务器开始工作。
initServer函数
该函数主要为初始化服务器,需要初始化的内容比较多,主要有:
1、创建事件循环
server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);
2、创建TCP与UDP Server,启动服务器,完成bind与listen
/* Open the TCP listening socket for the user commands. */ //server.ipfd是个int数组,启动服务器,完成bind,listen if (listenToPort(server.port,server.ipfd,&server.ipfd_count) == REDIS_ERR) exit(1); /* Open the listening Unix domain socket. */ 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); } }
Redis2.8.2 TCP同时支持IPv4与IPv6,同时与之前版本的Redis不同,此版本支持多个TCP服务器,listenToPort函数主要还是调用anetTcpServer函数,完成socket()–>bind()–>listen(),下面详细查看下TCPServer的创建,UDP直接忽略吧,我也不知道UDP具体用在哪。
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) { //绑定bind if (bind(s,sa,len) == -1) { anetSetError(err, "bind: %s", strerror(errno)); close(s); return ANET_ERR; } /* Use a backlog of 512 entries. We pass 511 to the listen() call because * the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); * which will thus give us a backlog of 512 entries */ //监听 if (listen(s, 511) == -1) { anetSetError(err, "listen: %s", strerror(errno)); close(s); return ANET_ERR; } return ANET_OK;}static int _anetTcpServer(char *err, int port, char *bindaddr, int af){ int s, rv; char _port[6]; /* strlen("65535") */ struct addrinfo hints, *servinfo, *p; snprintf(_port,6,"%d",port); memset(&hints,0,sizeof(hints)); hints.ai_family = af; hints.ai_socktype = SOCK_STREAM; //套接字地址用于监听绑定 hints.ai_flags = AI_PASSIVE; /* No effect if bindaddr != NULL */ //可以加上hints.ai_protocol = IPPROTO_TCP; /**getaddrinfo(const char *hostname, const char *servicename, const struct addrinfo *hint,struct addrinfo **res); hostname:主机名 servicename: 服务名 hint: 用于过滤的模板,仅能使用ai_family, ai_flags, ai_protocol, ai_socktype,其余字段为0 res:得到所有可用的地址 */ if ((rv = getaddrinfo(bindaddr,_port,&hints,&servinfo)) != 0) { anetSetError(err, "%s", gai_strerror(rv)); return ANET_ERR; } //轮流尝试多个地址,找到一个允许连接到服务器的地址时便停止 for (p = servinfo; p != NULL; p = p->ai_next) { if ((s = socket(p->ai_fam<em style="color:transparent">本文来源[email protected]搞@^&代*@码)网9</em>ily,p->ai_socktype,p->ai_protocol)) == -1) continue; if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error; //设置套接字选项setsockopt,采用地址复用 if (anetSetReuseAddr(err,s) == ANET_ERR) goto error; //bind, listen if (anetListen(err,s,p->ai_addr,p->ai_addrlen) == ANET_ERR) goto error; goto end; } if (p == NULL) { anetSetError(err, "unable to bind socket"); goto error; }error: s = ANET_ERR;end: freeaddrinfo(servinfo); return s;}//if server.ipfd_count = 0, bindaddr = NULLint anetTcpServer(char *err, int port, char *bindaddr){ return _anetTcpServer(err, port, bindaddr, AF_INET);}
3、将listen的端口加入到事件监听中,进行监听,由aeCreateFileEvent函数完成,其注册的listen端口可读事件处理函数为acceptTcpHandler,这样在listen端口有新连接的时候会调用acceptTcpHandler,后者在accept这个新连接,然后就可以处理后续跟这个客户端连接相关的事件了。
/* Create an event handler for accepting new connections in TCP and Unix * domain sockets. */ //文件事件,用于处理响应外界的操作请求,事件处理函数为acceptTcpHandler/acceptUnixHandler //在networking.c for (j = 0; j 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE, acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");