REmote DIctionary Server(Redis)是当前使用最广泛的高性能内存数据库,使用C语言开发,常常被用来实现缓存、分布式锁等功能。

本系列分析Redis 5.0版本,源代码可以从GitHub下载,付上下载地址

项目结构

Redis项目的目录结构如下:

  • redis/

    • deps/ 依赖项目

      • hiredis C语言客户端,Redis作者开发

      • jemalloc 内存管理

      • linenoise 命令行编辑库,Redis作者开发

      • lua Lua脚本

    • src/ 源代码目录

      • 100多个源代码文件

      • modules/ Redis module扩展模块的例子

    • tests/ 测试代码目录

    • utils/ 脚本程序目录

    • Makefile

    • redis.conf 默认配置文件

    • 其他文件

其中最重要的是src文件夹,包含了Redis服务端的源代码,本系列主要分析的代码都在这个文件夹下。

Redis服务器主程序入口在server.c文件,客户端的主程序入口在redis-cli.c文件中。

Redis如何处理请求

在深入细节之前,让我们先从主程序入口开始,对Redis的启动过程,以及如何处理客户端的指令有一个大致的了解。

server.c

int main(int argc, char **argv){
    // 省略代码,处理启动参数,初始化配置,指令列表,等等

    initServer();
    // 省略代码

    aeSetBeforeSleepProc(server.el,beforeSleep);
    aeSetAfterSleepProc(server.el,afterSleep);
    aeMain(server.el);  // 运行事件循环,处理请求
    aeDeleteEventLoop(server.el);
    return 0;
}

void initServer(void){
    // 省略代码

    // 开始监听端口
    if (server.port != 0 &&
        listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
        exit(1);
    // 省略代码

    // 创建定时事件
    if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
        serverPanic("Can't create event loop timers.");
        exit(1);
    }
    // 省略代码

    // 创建文件事件,处理TCP请求
    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.");
            }
    }
}

networking.c

void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;
    char cip[NET_IP_STR_LEN];
    UNUSED(el);
    UNUSED(mask);
    UNUSED(privdata);

    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);
    }
}

static void acceptCommonHandler(int fd, int flags, char *ip) {
    client *c;
    if ((c = createClient(fd)) == NULL) {  // 创建新的client
        serverLog(LL_WARNING,
            "Error registering fd event for the new client: %s (fd=%d)",
            strerror(errno),fd);
        close(fd);
        return;
    }
    // 省略代码
}

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

    if (fd != -1) {
        anetNonBlock(NULL,fd);
        anetEnableTcpNoDelay(NULL,fd);
        if (server.tcpkeepalive)
            anetKeepAlive(NULL,fd,server.tcpkeepalive);

        // 创建读取请求事件,注册处理请求的回调函数readQueryFromClient
        if (aeCreateFileEvent(server.el,fd,AE_READABLE,
            readQueryFromClient, c) == AE_ERR)
        {
            close(fd);
            zfree(c);
            return NULL;
        }
    }
    selectDb(c,0); //默认使用db0

    // 省略代码
}

main函数做了以下几件事情:

  • 处理程序运行参数
  • 配置并初始化server
  • 监听端口
  • 创建事件循环,注册回调函数
  • 运行事件循环

networking.c中createClient函数注册了回调函数readQueryFromClient,当服务器收到请求时会调用server.c中的call函数处理具体的请求,比如get,set,hmget等等。