redis源码解析

环境

  • redis1.0

redis早期版本代码不多,但包括redis核心功能,如事件驱动、线程模型、数据模型,适合阅读研究其核心结构

核心文件

  • anet
  1. anet负责基本的socket操作
  2. 包括连接建立、读、写,服务端端口监听、接收连接等
  3. 底层采用select多路复用器
  • ae
typedef struct aeEventLoop {
   
    long long timeEventNextId;
    aeFileEvent *fileEventHead;
    aeTimeEvent *timeEventHead;
    int stop;
} aeEventLoop;
  1. ae为事件驱动处理库
  2. 事件包括io事件以及定时事件
  • redis

redis主要负责客户端连接建立、命令处理、定时任务处理

线程模型

  • 网络编程主要包括建立与客户端的连接、读写事件、业务处理三个部分,根据实际的业务情况,可以把三部分分别或组合放在不同的线程中进行处理,由此产生了不同的线程模型。而redis采用单线程处理了连接建立、io读写、业务处理甚至还包括定时任务,这一切都基于其事件驱动模型。
  • aeEventLoop包括io(aeFileEvent)和定时任务(aeTimeEvent)两类事件,每类事件为一个链表队列,程序采用循环遍历事件队列进行事件处理。
void aeMain(aeEventLoop *eventLoop)
{
   
    eventLoop->stop = 0;
    while (!eventLoop->stop)
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}

数据模型

数据库

typedef struct redisDb {
   
    dict *dict;
    dict *expires;
    int id;
} redisDb;

  • redis默认有16个数据库,每个数据库包括两个hash表,一个用于存储无过期时间的kv,另一个用于存储有过期时间的kv

kv

typedef struct redisObject {
   
    void *ptr;
    int type;
    int refcount;
} robj;

● kv都为redisObject类型,该结构包括数据、数据的类型(String、List、Hash、Set)、引用数量,只是key的数据类型为String

主要数据结构

  • adlist

双向链表结构

  • dict
  1. dict为hash表,内部采用数组存储元素
  2. 出现hash冲突时,采用链表存储
  3. 每次扩容为原大小的2倍,实际大小为2n
  • sds

字符串操作库

核心方法

程序入口main

int main(int argc, char **argv) {
   
    initServerConfig();
    if (argc == 2) {
   
        ResetServerSaveParams();
        loadServerConfig(argv[1]);
    } else if (argc > 2) {
   
        fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");
        exit(1);
    } else {
   
        redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");
    }
    initServer();
    if (server.daemonize) daemonize();
    redisLog(REDIS_NOTICE,"Server started, Redis version " REDIS_VERSION);
#ifdef __linux__
    linuxOvercommitMemoryWarning();
#endif
    if (rdbLoad(server.dbfilename) == REDIS_OK)
        redisLog(REDIS_NOTICE,"DB loaded from disk");
    if (aeCreateFileEvent(server.el, server.fd, AE_READABLE,
        acceptHandler, NULL, NULL) == AE_ERR) oom("creating file event");
    redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
    aeMain(server.el);
    aeDeleteEventLoop(server.el);
    return 0;
}

  1. 程序入口为redis.c的main方法
  2. initServer方法中主要包括绑定端口、建立网络监听、入队定时任务事件
  3. 开启事件循环

建立连接

static void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
   
    int cport, cfd;
    char cip[128];
    redisClient *c;
    REDIS_NOTUSED(el);
    REDIS_NOTUSED(mask);
    REDIS_NOTUSED(privdata);

    cfd = anetAccept(server.neterr, fd, cip, &cport);
    if (cfd == AE_ERR) {
   
        redisLog(REDIS_DEBUG,"Accepting client connection: %s", server.neterr);
        return;
    }
    redisLog(REDIS_DEBUG,"Accepted %s:%d", cip, cport);
    if ((c = createClient(cfd)) == NULL) {
   
        redisLog(REDIS_WARNING,"Error allocating resoures for the client");
        close(cfd); /* May be already closed, just ingore 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 nonblocking
     * mode and we can send an error for free using the Kernel I/O */
    if (server.maxclients && 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 */
        (void) write(c->fd
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值