Redis的主要脉络梳理

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。


由于工作需要,需要深入研究此开源代码用于选型及设计代码,先从main函数开始吧。

参考版本:Redis-3.2.3


首先从main函数开始@server.c

int main(int argc, char **argv) {
	
	// 全局变量 struct redisServer server; /* server global state */ 初始化
	initServerConfig();
	
    /* Check if we need to start in redis-check-rdb mode. We just execute
     * the program main. However the program is part of the Redis executable
     * so that we can easily execute an RDB check on loading errors. */
    if (strstr(argv[0],"redis-check-rdb") != NULL)
        redis_check_rdb_main(argc,argv);
    
    // 重置server.saveparams
    resetServerSaveParams();
    
    // 加载redis.conf作为配置文件
    loadServerConfig(configfile,options);
	
	// 决定是否以daemon方式启动redis
	server.supervised = redisIsSupervised(server.supervised_mode);
    int background = server.daemonize && !server.supervised;
    if (background) 
    	daemonize();
	
	// 初始化服务器--主要工作是createSharedObjects();及aeCreateEventLoop创建事件Loop
	initServer();
	
	// 判断是否需要从aof文件或者rdb装载数据
	if (!server.sentinel_mode) {
		loadDataFromDisk();
		...
	}else{
		sentinelIsRunning();
	}
	
	// 设置每次进入事件处理函数之前需要执行的函数
	aeSetBeforeSleepProc(server.el,beforeSleep);
	
	// 进入事件循环主函数,永不退出,除非服务被终止
    aeMain(server.el);
    
    // main函数结束、服务器退出
    aeDeleteEventLoop(server.el);
    return 0;
}


下面对子过程进行详细分析:
1、客户端连接处理
initServer()@server.c 这个函数中会调用anetTcpServer和anetUnixServer,分别建立tcp端口和unix域套接字的监听。

anetTcpServer@anet.c
int anetTcpServer(char *err, int port, char *bindaddr, int backlog)
{
    return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);
}
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog)
{
	// 建立TCP套接字监听
	s = socket(p->ai_family,p->ai_socktype,p->ai_protocol);
	anetSetReuseAddr(err,s);
	anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog);
	
	return s;
}
以上只是设置要监听的端口、地址、和地址族,再调用anetListen()函数绑定地址并监听端口,这些工作完成后
anetTcpServer 函数返回,并将创建的套接字复制给server.ipfd.

listenToPort(server.port,server.ipfd,&server.ipfd_count)
-->
int listenToPort(int port, int *fds, int *count) {
fds[*count] = anetTcpServer(server.neterr,port,server.bindaddr[j],
                server.tcp_backlog);
}

前面提到有一个EventLoop的事件队列、这个fd将会在此被使用。

void initServer(void) {
	// 建立主事件队列--EventLoop
	server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
	
	...
	
    /* Create an event handler for accepting new connections in TCP and Unix
     * domain sockets. */
    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.");
            }
    }
	
}

首先,从eventLoop的event这个aeFileEvent数组里,取出当前fd对应的acFileEvent,
主要是为了在下边给它设置对应事件的处理函数;即根据传入的mask来判断是哪一类事件。
这里就是 server.ipfd 加入事件监控中、当发生AE_READABLE可读事件时则触发 acceptTcpHandler 函数。

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
        aeFileProc *proc, void *clientData)
{
    if (fd >= eventLoop->setsize) {
        errno = ERANGE;
        return AE_ERR;
    }
    aeFileEvent *fe = &eventLoop->events[fd];
	// 加入异步事件监控中
    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;
    fe->mask |= mask;
    if (mask & AE_READABLE) fe->rfileProc = proc;
    if (mask & AE_WRITABLE) fe->wfileProc = proc;
    fe->clientData = clientData;
    if (fd > eventLoop->maxfd)
        eventLoop->maxfd = fd;
    return AE_OK;
}

此时监控建立完毕、此时客户端一个新的连接到来,此时将会触发事件。

主循环事件函数:aeMain@Ae.c

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    // 只要stop标志一直为0则循环永远运行下去
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }	
}

/* Process every pending time event, then every pending file event
 * (that may be registered by time event callbacks just processed).
 * Without special flags the function sleeps until some file event
 * fires, or when the next time event occurs (if any).
 *
 * If flags is 0, the function does nothing and returns.
 * if flags has AE_ALL_EVENTS set, all the kind of events are processed.
 * if flags has AE_FILE_EVENTS set, file events are processed.
 * if flags has AE_TIME_EVENTS set, time events are processed.
 * if flags has AE_DONT_WAIT set the function returns ASAP until all
 * the events that's possible to process without to wait are processed.
 *
 * The function returns the number of events processed. */
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
	// 最重要的轮询函数!!!
    numevents = aeApiPoll(eventLoop, tvp);
    for (j = 0; j < numevents; j++) {
        aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
        int mask = eventLoop->fired[j].mask;
        int fd = eventLoop->fired[j].fd;
        int rfired = 0;

    	/* note the fe->mask & mask & ... code: maybe an already processed
         * event removed an element that fired and we still didn't
         * processed, so we check if the event is still valid. */
        if (fe->mask & mask & AE_READABLE) {
            rfired = 1;
            fe->rfileProc(eventLoop,fd,fe->clientData,mask); // 可读回调
        }
        if (fe->mask & mask & AE_WRITABLE) {
            if (!rfired || fe->wfileProc != fe->rfileProc)
                fe->wfileProc(eventLoop,fd,fe->clientData,mask); // 可写回调
        }
        processed++;
    }
}

客户端新的连接则触发可读事件、即acceptTcpHandler函数

void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    int cport, cfd, max = MAX_ACCEPTS_PER_CALL; // 当前redis服务器的最大连接总数
    char cip[NET_IP_STR_LEN];

    while(max--) {
        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
        if (cfd == ANET_ERR) {
            if (errno != EWOULDBLOCK)
                serverLog(LL_WARNING,
                    "Accepting client connection: %s", server.neterr);
            return;
        }
        serverLog(LL_VERBOSE,"Accepted %s:%d", cip, cport);
        
        // 接收新的连接后处理函数
        acceptCommonHandler(cfd,0,cip);
    }
}
利用anetTcpAccept产生新的fd并加入到事件队列中

static void acceptCommonHandler(int fd, int flags, char *ip) {
    client *c = createClient(fd);
	// 客户端数目限定
    if (listLength(server.clients) > server.maxclients) {
        char *err = "-ERR max number of clients reached\r\n";
		..
	}	
}

-->
client *createClient(int fd) {
    client *c = zmalloc(sizeof(client));

    /* passing -1 as fd it is possible to create a non connected client.
     * This is useful since all the 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. */
    if (fd != -1) {
        anetNonBlock(NULL,fd);
        anetEnableTcpNoDelay(NULL,fd);
        if (server.tcpkeepalive)
            anetKeepAlive(NULL,fd,server.tcpkeepalive);
        // 给前面获取的客户端连接的套接字注册 AE_READABLE事件,并设置事件处理函数readQueryFromClient
        if (aeCreateFileEvent(server.el,fd,AE_READABLE,
            readQueryFromClient, c) == AE_ERR)
        {
            close(fd);
            zfree(c);
            return NULL;
        }
    }
	...
}

这里结束后则 readQueryFromClient()便是redis处理客户端发送的命令的起始点了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值