Redis is introduced

Redis is implemented by C language, is completely open source and free, complies with BSD protocol, is a high-performance key-value database. Redis-1.0 is the first stable release and has only 8K + lines of code. Although the amount of code is not large, but the implementation of many functions:

  • Data persistence is supported
  • Supports the master and Slave functions
  • Value Supports String, List, and Set data structures

Because of the small amount of code, clear structure, rich features and other characteristics, RedIS-1.0 is recommended by many netizens, suitable for beginners to read and learn.

Redis each version of the source code download address is as follows: the download. Redis. IO/releases /

Gz and run the tar -zxvf redis-1.0.tar.gz command to decompress the file. In the unzipped folder, there is a doc folder that describes the redis installation, the use of each command, and so on. It is recommended to read readme. HTML first, compile, install and run according to the steps in it, connect redis server with Telnet, use SET, GET and other instructions, have a preliminary understanding of Redis.

The overall flow of Redis1.0

The main function

When reading Redis1.0, you can start with the main function, in the file redis.c.

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;
}
Copy the code

The initServerConfig function initializes the server configuration with default values, such as 16 DB numbers and port number 6379. Of course, Redis supports specifying configuration files at startup. For details, see the redis.conf file. If the configuration file is specified, the startup script is changed to./redis-server./redis.conf. When you specify a configuration file, the loadServerconfig function reads the file and sets the information, such as the port number. The initServer function establishes socket listening and initializes time-driven events. The rdbLoad function reads db data into memory. Next, the aeCreatFileEvent function is called to add the socket aceeptHandler to the el field of the global variable Server. Finally, the aeMain function is called to execute a while (1)loop.

Before moving on to the implementation of each function, take a look at an important global variable in Redis :server, whose type is a structure called redisServer. Port holds the port number that Redis listens on, fd holds the socket that Redis opens, and is assigned a value in initServer. Clients Holds the connected client, which is a bidirectional linked list. El stores all kinds of events, including file events (AefileEvents) and time events (AetimeEvents), both of which are maintained by one-way linked lists. The structure redisServer also includes other fields, which are not covered here and will be explained later when we introduce the specific functionality. RedisServer interception is defined as follows:

/* Global server state structure */
struct redisServer {
    int port;
    int fd;
    list *clients;
    aeEventLoop *el;
};
Copy the code

initServer

After redis configuration is set, initServer is called to initialize the server. The initServer function is responsible for initialization, including creating a list of client connections, a list of Slaves, listening ports, initialization time driven events, etc. Server.el = aeCreateEventLoop() creates an event management structure, Server. fd = anetTcpServer(Server.net err, server.port, server.bindAddr) Establishes socket listening. AeCreateTimeEvent (server.el, 1000, serverCron, NULL, NULL) creates a linked list of time events.

static void initServer(a) {
    int j;

    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    setupSigSegvAction();

    server.clients = listCreate();
    server.slaves = listCreate();
    server.monitors = listCreate();
    server.objfreelist = listCreate();
    createSharedObjects();
    server.el = aeCreateEventLoop();
    server.db = zmalloc(sizeof(redisDb)*server.dbnum);
    server.sharingpool = dictCreate(&setDictType,NULL);
    if(! server.db || ! server.clients || ! server.slaves || ! server.monitors || ! server.el || ! server.objfreelist) oom("server initialization"); /* Fatal OOM */
    server.fd = anetTcpServer(server.neterr, server.port, server.bindaddr);
    if (server.fd == - 1) {
        redisLog(REDIS_WARNING, "Opening TCP port: %s", server.neterr);
        exit(1);
    }
    for (j = 0; j < server.dbnum; j++) {
        server.db[j].dict = dictCreate(&hashDictType,NULL);
        server.db[j].expires = dictCreate(&setDictType,NULL);
        server.db[j].id = j;
    }
    server.cronloops = 0;
    server.bgsaveinprogress = 0;
    server.bgsavechildpid = - 1;
    server.lastsave = time(NULL);
    server.dirty = 0;
    server.usedmemory = 0;
    server.stat_numcommands = 0;
    server.stat_numconnections = 0;
    server.stat_starttime = time(NULL);
    aeCreateTimeEvent(server.el, 1000, serverCron, NULL.NULL);
}
Copy the code

aeMain

EventLoop ->stop is not equal to zero. AeProcessEvents is used to handle the eventLoop.

void aeMain(aeEventLoop *eventLoop)
{
    eventLoop->stop = 0;
    while(! eventLoop->stop) aeProcessEvents(eventLoop, AE_ALL_EVENTS); }Copy the code

EventLoop is the el field of the global variable Server and is of type structure aeEventLoop.

/* State of an event based program */
typedef struct aeEventLoop {
    long long timeEventNextId;
    aeFileEvent *fileEventHead;     
    aeTimeEvent *timeEventHead;     
    int stop;
} aeEventLoop;
Copy the code

Next, we’ll focus on what the function aeProcessEvents does.

First, the linked list fileEventHead is iterated to obtain the collections of AE_READABLE, AE_WRITABLE, and AE_EXCEPTION.

aeFileEvent *fe = eventLoop->fileEventHead;
/* Check file events */
if (flags & AE_FILE_EVENTS) {
    while(fe ! =NULL) {
        if (fe->mask & AE_READABLE) FD_SET(fe->fd, &rfds);
        if (fe->mask & AE_WRITABLE) FD_SET(fe->fd, &wfds);
        if (fe->mask & AE_EXCEPTION) FD_SET(fe->fd, &efds);
        if(maxfd < fe->fd) maxfd = fe->fd; numfd++; fe = fe->next; }}Copy the code

Next, calculate the remaining time TV triggered by the last time event and use this time as the timeout parameter of the select

if(flags & AE_TIME_EVENTS && ! (flags & AE_DONT_WAIT)) shortest = aeSearchNearestTimer(eventLoop);if (shortest) {
    long now_sec, now_ms;
    /* Calculate the time missing for the nearest * timer to fire. */
    aeGetTime(&now_sec, &now_ms);
    tvp = &tv;
    tvp->tv_sec = shortest->when_sec - now_sec;
    if (shortest->when_ms < now_ms) {
        tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;
        tvp->tv_sec --;
    } else {
        tvp->tv_usec = (shortest->when_ms - now_ms)*1000; }}else {
    /* If we have to check for events but need to return * ASAP because of AE_DONT_WAIT we need to se the timeout * to zero * /
    if (flags & AE_DONT_WAIT) {
        tv.tv_sec = tv.tv_usec = 0;
        tvp = &tv;
    } else {
        /* Otherwise we can block */
        tvp = NULL; /* wait forever */}}Copy the code

Call SELECT to get the number of file descriptors that are readable and writable. If there are read-write file descriptors, the linked list fileEventHead in eventLoop is iterated over, calling the fielProc function on each node in turn. FileEventHead each node of the linked list is of type struct aeFileEvent. The function to create the node is: int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData, AeEventFinalizerProc * finalizerProc). Proc of the function argument, specifying the node’s fileProc implementation. By searching globally where the function aeCreateFileEvent is called, we can see that there are four specific implementations of fileProc on nodes: ReadQueryFromClient, sendReplyToClient, sendBulkToSlave, acceptHandler.

retval = select(maxfd+1, &rfds, &wfds, &efds, tvp);
if (retval > 0) {
    fe = eventLoop->fileEventHead;
    while(fe ! =NULL) {
        int fd = (int) fe->fd;
       if ((fe->mask & AE_READABLE && FD_ISSET(fd, &rfds)) ||
            (fe->mask & AE_WRITABLE && FD_ISSET(fd, &wfds)) ||
            (fe->mask & AE_EXCEPTION && FD_ISSET(fd, &efds)))
        {
            int mask = 0;
            if (fe->mask & AE_READABLE && FD_ISSET(fd, &rfds))
                 mask |= AE_READABLE;
            if (fe->mask & AE_WRITABLE && FD_ISSET(fd, &wfds))
                mask |= AE_WRITABLE;
            if (fe->mask & AE_EXCEPTION && FD_ISSET(fd, &efds))
                mask |= AE_EXCEPTION;
            /* * fileProce = readQueryFromClient, sendReplyToClient, sendBulkToSlave, acceptHandler */
            fe->fileProc(eventLoop, fe->fd, fe->clientData, mask);
            processed++;
            /* After an event is processed our file event list * may no longer be the same, so what we do * is to clear the bit for this file descriptor and * restart again from the head. */
            fe = eventLoop->fileEventHead;
            FD_CLR(fd, &rfds);  // FD_CLR(fd_set *fdset); Deletes a file descriptor from a file descriptor collection.
            FD_CLR(fd, &wfds);
            FD_CLR(fd, &efds);
        } else{ fe = fe->next; }}}Copy the code

After processing the socket event, it is processing time event. As with sockets, you iterate through the linked list timeEventHead in eventLoop, calling the timeProc function on each node in turn. Each node of timeEventHead is of type struct aeTimeEvent, Long long aeCreateTimeEvent(aeEventLoop *eventLoop, long Long milliseconds, aeTimeProc *proc, void *clientData, AeEventFinalizerProc *finalizerProc). AeCreateTimeEvent (server.el, 1000, serverCron, NULL, NULL); .

/* Check time events */
if (flags & AE_TIME_EVENTS) {
    te = eventLoop->timeEventHead;
    maxId = eventLoop->timeEventNextId- 1;
    while(te) {
        long now_sec, now_ms;
        long long id;
       if (te->id > maxId) {
            te = te->next;
            continue;
        }
        aeGetTime(&now_sec, &now_ms);
        if (now_sec > te->when_sec ||
            (now_sec == te->when_sec && now_ms >= te->when_ms))
        {
            int retval;

            id = te->id;
            retval = te->timeProc(eventLoop, id, te->clientData);
            /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever. * To do so we saved the max ID we want to handle. */
            if(retval ! = AE_NOMORE) { aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); }else {
                aeDeleteTimeEvent(eventLoop, id);
            }
            te = eventLoop->timeEventHead;
        } else{ te = te->next; }}}Copy the code

In subsequent chapters, the following will be explained in turn:

Redis1.0 communicates with the client

Redis memory data management