redis源代码分析16–阻塞式命令

redis现在只支持对list的阻塞式操作,相关的两个命令是brpop和blpop。

这两个命令在list中有元素时,跟普通的pop没有区别,弹出list的一个元素,然后返回。但在list没有元素时,会为redisClient设置REDIS_BLOCKED标志,然后client阻塞(设置REDIS_BLOCKED标志的redisClient会一直阻塞,参考命令处理章节),一直到新元素加入时(push操作的处理函数pushGenericCommand),才会返回。

这两个命令设置的处理函数brpopCommand和blpopCommand都会调用blockingPopGenericCommand。该函数在检查list中有元素后,会调用非阻塞的popGenericCommand来弹出一个元素,否则调用blockForKeys来处理阻塞的情况。 继续阅读

发表在 redis | 一条评论

redis源代码分析15–val加载机制

这一节主要介绍下val的加载。

对于某些命令,比如get somekey,当运行到processCommand时可能key对应的val不在内存中。在运行命令绑定的处理函数之前,redis会提前加载其val。

在 processCommand中,在vm开启并启用多线程时,会调用 blockClientOnSwappedKeys来加载可能已swap的val,如果blockClientOnSwappedKeys返回0,说明有 swap的val没被加载,则返回不调用call了(此时client会设置 REDIS_IO_WAIT标志,并已放到等待列表中)。代码如下: 继续阅读

发表在 redis | 一条评论

redis源代码分析14–命令处理的一般过程

这个部分我们介绍下命令处理的一般过程。
在createClient时,为client的read事件设置了readQueryFromClient函数。我们来看看怎么处理client的命令的。
readQueryFromClient使用read一次读入REDIS_IOBUF_LEN字节,并保存在client中的querybuf参数中,然后调用processInputBuffer继续处理。 继续阅读

发表在 redis | 一条评论

redis源代码分析13–client连接(下)

这一节我们介绍下client连接的几个标志。

client的flags的取值有如下几种:

#define REDIS_SLAVE 1      /* This client is a slave server */
#define REDIS_MASTER 2     /* This client is a master server */
#define REDIS_MONITOR 4    /* This client is a slave monitor, see MONITOR */
#define REDIS_MULTI 8      /* This client is in a MULTI context */
#define REDIS_BLOCKED 16   /* The client is waiting in a blocking operation */
#define REDIS_IO_WAIT 32   /* The client is waiting for Virtual Memory I/O */

redis支持主从复制、监控特性,对这些server的连接状态也是保存在当前server的redisClient结构中, 并在redisClient的flags标志中设置REDIS_SLAVE 、REDIS_MASTER、 REDIS_MONITOR参数。我们在后续的章节中详细分析redis的主从复制、监控等特性。

一般的client仅会设置REDIS_MULTI、REDIS_BLOCKED、REDIS_IO_WAIT中的1个或多个。REDIS_MULTI跟redis的事务支持相关,我们后续介绍。REDIS_BLOCKED标志跟redis支持的list阻塞式pop(BLPOP、BRPOP)有关,也就是当list为空的时候,会阻塞client,一直到有元素加入list,此时再pop。REDIS_IO_WAIT跟命令字有关,对于某些命令,如果启用vm的话,需要提前加载其key。我们在命令处理章节中分析。

发表在 redis | 留下评论

redis源代码分析12–client连接(中)

这一节我们简略介绍下client连接的3个核心函数。涉及的很多参数跟redis的诸多特性有关,可在阅读后续章节后返回查看其相关值。

redis将新client的连接状态保存在redisClient结构体中,该结构体的代码如下: 继续阅读

发表在 redis | 一条评论

redis源代码分析11–client连接(上)

接下来的三节我们介绍client连接,按接受client连接、client连接的3个核心函数、client连接的几个标志3个部分顺序介绍。

这一节我们介绍下redis如何接受client连接。

在main函数中调用的initServer中,可看到如下代码:

static void initServer() {
  ---
   server.fd = anetTcpServer(server.neterr, server.port, server.bindaddr);
  ---
   if (aeCreateFileEvent(server.el, server.fd, AE_READABLE,
        acceptHandler, NULL) == AE_ERR) oom("creating file event");
  ---
}

继续阅读

发表在 redis | 留下评论

redis源代码分析10–事件处理(下)

serverCron做的工作很多,后续的很多章节都与此有关。该函数较复杂,分段分析。
一开始将当前时间保存,方便后续vm等机制对当前时间的访问:

/* We take a cached value of the unix time in the global state because
* with virtual memory and aging there is to store the current time
* in objects at every object access, and accuracy is not needed.
* To access a global var is faster than calling time(NULL) */
// 缓存时间
server.unixtime = time(NULL);

接着,如果收到SIGTERM等信号,则会在信号处理函数中设置server.shutdown_asap为1,此时就会调用prepareForShutdown做结束运行前的结尾工作: 继续阅读

发表在 redis | 留下评论

redis源代码分析9–事件处理(中)

接下来,我们分析下redis中事件的处理逻辑。

在函数initServer中调用aeCreateEventLoop完成初始化后,在main函数中调用ae_main,该函数是一个死循环:

static void initServer() {
    ---
    server.el = aeCreateEventLoop();
    ---
}

int main(int argc, char **argv) {
   ---
   initServer();
   ---
   aeSetBeforeSleepProc(server.el,beforeSleep);
   aeMain(server.el);
   ---
}

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}

继续阅读

发表在 redis | 8 条评论

redis源代码分析8–事件处理(上)

redis是单进程单线程事件多路循环处理所有的客户端连接,它的运行都是靠事件触发的。

redis 的事件处理支持select、kqueue、epoll机制。其核心的poll函数aeApiPoll其实是一个封装函数,最终是调用 ae_select.c、ae_epoll.c还是ae_kqueue.c中的aeApiPoll(分别实现select、kqueue、epoll机制),取决于如下的宏定义:

#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
    #ifdef HAVE_KQUEUE
    #include "ae_kqueue.c"
    #else
    #include "ae_select.c"
    #endif
#endif

ae_select.c、ae_epoll.c、ae_kqueue.c分别对select、kqueue、epoll进制进行了封装,对select、kqueue、epoll的性能比较可在网上找到详细资料。

所有的事件保存在server.el中,el是如下的一个结构: 继续阅读

发表在 redis | 一条评论

redis源代码分析7–内存(下)

上一节提到的used_memory变量保存了redis当前所使用的内存。其值常用来跟server.vm_max_memory、server.maxmemory进行比较。vm_max_memory表示redis vm启动swap的内存阈值,在超过该值后应启动vm的swap操作;maxmemory表示redis允许分配的最大内存,在超过该值后应进行内存的释放。这些比较主要在rdbLoad、loadAppendOnlyFile、serverCron、processCommand、vmThreadedIOCompletedJob等函数中。值得注意的是,尽管redis会尽量将内存使用量降低到server.maxmemory(甚至server.vm_max_memory)之下,但并不对此保证。 继续阅读

发表在 redis | 2 条评论