件监听中,进行监听,由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 < 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.");
}
}
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");
acceptTcpHandler函数
上面介绍了,initServer完成listen端口后,会加入到事件循环中,该事件为可读事件,并记录处理函数为fe->rfileProc = acceptTcpHandler;该函数分两步操作:用acceptTcpHandler接受这个客户端连接;然第二部初始化这个客户端连接的相关数据,将clientfd加入事件里面,设置的可读事件处理函数为readQueryFromClient,也就是读取客户端请求的函数。
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);
//cfd为accept函数返回的客户端文件描述符,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);
//将cfd加入事件循环并设置回调函数为readQueryFromClient,并初始化redisClient
acceptCommonHandler(cfd,0);
}
第一步很简单即完成accept,主要关注第二步acceptCommonHandler函数
static void acceptCommonHandler(int fd, int flags) {
redisClient *c;
if ((c = createClient(fd)) == NULL) {//创建新的客户端
redisLog(REDIS_WARNING,
"Error registering fd event for the new client: %s (fd=%d)",
strerror(errno),fd);
close(fd); /* May be already closed, just ignore errors */
return;
}
/* If maxclient directive is set and this is one client more... close the
* connection. Note that we create the client instead to check before
* for this condition, since now the socket is already set in non-blocking
* mode and we can send an error for free using the Kernel I/O */
//当前连接的客户端数目大于服务器最大运行的连接数,则拒绝连接
if (listLength(server.clients) > server.maxclients) {
char *err = "-ERR max number of clients reached\r\n";
/* That's a best effort error message, don't check write errors */
if (write(c->fd,err,strlen(err)) == -1) {
/* Nothing to do, Just to avoid the warning... */
}
server.stat_rejected_conn++;
freeClient(c);
return;
}
server.stat_numconnections++;
c->flags |= flags;
}
createClient函数
此函数用来为新连接的客户端初始化一个redisClient数据结构,该数据结构有比较多的参数,详见redis.h。该函数完成两个操作,第一、为客户端创建事件处理函数readQueryFromClient专门接收客户端发来的指令,第二、初始化redisClient数据结构相关参数。
redisClient *createClient(int fd) {
redisClient *c = zmalloc(sizeof(redisClient));
/* passing -1 as fd it is possible to create a non connected client.
* This is useful since all the Redis commands needs to be executed
* in the context of a client. When commands are executed in other
* contexts (for instance a Lua script) we need a non connected client. */
/**
因为 Redis 命令总在客户端的上下文中执行,
有时候为了在服务器内部执行命令,需要使用伪客户端来执行命令
在 fd == -1 时,创建的客户端为伪终端
*/
if (fd != -1) {
//下面三个都是设置socket属性
anetNonBlock(NULL,fd);//非阻塞
anetEnableTcpNoDelay(NULL,fd);//no delay
if (server.tcpkeepalive)
anetKeepAlive(NULL,fd,server.tcpkeepalive);//keep alive
//创建一个accept fd的FileEvent事件,事件的处理函数是readQueryFromClient
if (aeCreateFileEvent(server.