您的位置:首页 > 数据库 > Redis

Redis源码分析:服务器编程模型

2011-12-13 23:33 435 查看
Base:redis 2.4.4

Update : redis 2.8.7

Reids基于事件驱动(Event-driven)构建服务。

Event-driven,提到这个,很容易想到如雷贯耳的libevent库(libeven封装了以下三种事件的响应:IO事件,定时器事件,信号事件)。

Redis的没有采用庞大的libevent库,而是自己写了一个,牺牲了一些平台通用性,但是性能非常强劲。memcache采用了libevent,有人认为这是redis的优于性能比memcache性能。没有系统性的测试过,此处保留意见。

x相关源码:ae.h ae.c networking.c anet.c net.h ae_epoll.c ae_select.c ae_kqueue.c

ae.h、ae.c :event library具体实现

networking.c : 与客户端的交互

anet.h anet.c : 网络通信

ae_epoll.c ae_select.c ae_kqueue.c : 不同系统多路IO封装

ae_epoll.c : linux平台

ae_select.c :unix平台

ae_kqueue.c : BSD、APPLE



根据操作系统进行多路IO类型选择,linux系统选择为epoll


#ifdef __linux__
#define H***E_EPOLL 1
#endif

#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define H***E_KQUEUE 1
#endif

#ifdef __sun
#include <sys/feature_tests.h>
#ifdef _DTRACE_VERSION
#define H***E_EVPORT 1
#endif
#endif
#ifdef H***E_EVPORT
#include "ae_evport.c"
#else
    #ifdef H***E_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef H***E_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endif


其中ae_epoll.c中封装了epoll的实现

多路IO封装

aeApiCreate:创建句柄(epoll_create)

aeApiFree:关闭句柄(close)

aeApiAddEvent:事件添加(epoll_ctl)

aeApiDelEvent:事件删除(epoll_ctl)

aeApiPoll:等待事件发生(epoll_wait)

服务处理

1. 初始化server, 等待客户端连接,并注册事件,回调函数acceptTcpHandler/acceptUnixHandler

if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
        acceptTcpHandler,NULL) == AE_ERR) oom("creating file event");
    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
        acceptUnixHandler,NULL) == AE_ERR) oom("creating file event");
2. 回调函数acceptTcpHandler/acceptUnixHandler

在监听到新连接请求时,接收连接,创建redisClient对象,并注册事件(回调函数readQueryFromClient)

void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    ...... 
    nread = read(fd, c->querybuf+qblen, readlen);
    ......
    if (nread) {
        sdsIncrLen(c->querybuf,nread);
        c->lastinteraction = server.unixtime;
        if (c->flags & REDIS_MASTER) c->reploff += nread;
    } else {
        server.current_client = NULL;
        return;
    }
    ......
}
3. 客户端请求处理:

在接收到客户端请求数据后,首先对请求进行分析,解析完成后反馈请求

void processInputBuffer(redisClient *c) {
    while(sdslen(c->querybuf)) {
        ...... //分析请求
        if (c->argc == 0) {
            resetClient(c);
        } else {
            if (processCommand(c) == REDIS_OK)
                resetClient(c);
        }
    }
}


在请求处理完成后,反馈结果.

Redis执行完客户端请求后,会调用addReply,在addReply中调用installWriteEvent来注册一个事件,并绑定事件处理函数sendReplyToClient,用来把响应发送到client

4.主循环

处理定时事件和注册事件

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}
beforesleep通过aeSetBeforeSleepProc定义,主要是特殊处理vm和aof相关的请求

rfileProc和wfileProc即注册事件时定义的回调函数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: