概述

redis的事件机制主要包括两类事件:文件事件(网络io事件)和定时器事件。redis的事件机制归根结底就是利用了epoll_wait来实现网络IO事件和定时器事件,通过epoll_wait的events参数监听了一系列网络IO事件,通过epoll_wait的timeout参数监听了定时器事件,每次从epoll_wait返回,或者是IO事件被触发,或者是定时器事件被触发



数据结构

核心数据结构如下:

typedef struct aeEventLoop {
    int maxfd;   /* highest file descriptor currently registered */
    int setsize; /* max number of file descriptors tracked */
    long long timeEventNextId;
    time_t lastTime;     /* Used to detect system clock skew */
    aeFileEvent *events; /* Registered events */
    aeFiredEvent *fired; /* Fired events */
    aeTimeEvent *timeEventHead;
    int stop;
    void *apidata; /* This is used for polling API specific data */
    aeBeforeSleepProc *beforesleep;
} aeEventLoop;
typedef struct aeEventLoop {
    int maxfd;   /* highest file descriptor currently registered */
    int setsize; /* max number of file descriptors tracked */
    long long timeEventNextId;
    time_t lastTime;     /* Used to detect system clock skew */
    aeFileEvent *events; /* Registered events */
    aeFiredEvent *fired; /* Fired events */
    aeTimeEvent *timeEventHead;
    int stop;
    void *apidata; /* This is used for polling API specific data */
    aeBeforeSleepProc *beforesleep;
} aeEventLoop;

其中,maxfd表示已监控的fd的最大值;setsize表示能够监控的fd的最大数量;timeEventNextId表示下一个定时器timer的id,id从0开始,每创建一个timer,id加1;lastTime用于检测系统事件的偏差;events是已经注册了的文件事件列表;fired是已经触发了的文件事件列表;timeEventHead是定时器列表;stop是停止标志,若其被设置,则退出eventloop;apidata中存储epoll fd和事件列表;beforesleep是阻塞的等待下一次事件发生之前要执行的函数。

整体数据结构如下:



文件事件

文件事件数据结构如下:

typedef struct aeFileEvent {
    int mask; /* one of AE_(READABLE|WRITABLE) */
    aeFileProc *rfileProc;
    aeFileProc *wfileProc;
    void *clientData;
} aeFileEvent;
typedef struct aeFileEvent {
    int mask; /* one of AE_(READABLE|WRITABLE) */
    aeFileProc *rfileProc;
    aeFileProc *wfileProc;
    void *clientData;
} aeFileEvent;

其中,mask表示事件掩码,读或者写;rfileProc表示读事件处理函数;wfileProc表示写事件处理函数;clientData是传递给rfileProc和wfileProc的数据



定时器事件

定时器事件数据结构如下:

typedef struct aeTimeEvent {
    long long id; /* time event identifier. */
    long when_sec; /* seconds */
    long when_ms; /* milliseconds */
    aeTimeProc *timeProc;
    aeEventFinalizerProc *finalizerProc;
    void *clientData;
    struct aeTimeEvent *next;
} aeTimeEvent;
typedef struct aeTimeEvent {
    long long id; /* time event identifier. */
    long when_sec; /* seconds */
    long when_ms; /* milliseconds */
    aeTimeProc *timeProc;
    aeEventFinalizerProc *finalizerProc;
    void *clientData;
    struct aeTimeEvent *next;
} aeTimeEvent;

其中,id是定时器id;when_sec和when_ms是定时器下次触发的时间;timeProc是定时器处理函数;finalizerProc是删除定时器时调用的函数;clientData是传递给timeProc和finalizerProc的参数;next是指向下一个定时器的指针。



api

aeEventLoop *aeCreateEventLoop(int setsize);            //创建事件循环体结构
void aeDeleteEventLoop(aeEventLoop *eventLoop);         //删除事件循环体结构
void aeStop(aeEventLoop *eventLoop);                    //结束事件循环

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 
        aeFileProc *proc, void *clientData);                         //创建文件事件
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);    //删除文件事件
int aeGetFileEvents(aeEventLoop *eventLoop, int fd);                 //获取文件事件掩码

long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
        aeTimeProc *proc, void *clientData,
        aeEventFinalizerProc *finalizerProc);                    //创建定时器事件
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);     //删除定时器事件

int aeProcessEvents(aeEventLoop *eventLoop, int flags);          //事件处理主函数,先处理文件事件,在处理定时器事件
int aeWait(int fd, int mask, long long milliseconds);            //阻塞等待fd上面的事件发生
void aeMain(aeEventLoop *eventLoop);                             //事件主循环,直到调用了aeStop结束循环
char *aeGetApiName(void);                                        //获取具体平台的多路io机制名
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);    //设置beforesleep函数
aeEventLoop *aeCreateEventLoop(int setsize);            //创建事件循环体结构
void aeDeleteEventLoop(aeEventLoop *eventLoop);         //删除事件循环体结构
void aeStop(aeEventLoop *eventLoop);                    //结束事件循环

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 
        aeFileProc *proc, void *clientData);                         //创建文件事件
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);    //删除文件事件
int aeGetFileEvents(aeEventLoop *eventLoop, int fd);                 //获取文件事件掩码

long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
        aeTimeProc *proc, void *clientData,
        aeEventFinalizerProc *finalizerProc);                    //创建定时器事件
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);     //删除定时器事件

int aeProcessEvents(aeEventLoop *eventLoop, int flags);          //事件处理主函数,先处理文件事件,在处理定时器事件
int aeWait(int fd, int mask, long long milliseconds);            //阻塞等待fd上面的事件发生
void aeMain(aeEventLoop *eventLoop);                             //事件主循环,直到调用了aeStop结束循环
char *aeGetApiName(void);                                        //获取具体平台的多路io机制名
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);    //设置beforesleep函数



应用

#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
#include "ae.h"

using namespace std;

#define MAX_CLIENTS 1024
#define LISTEN_BACKLOG 10
#define BUFFER 10240

void Die(const char *str) {
    cerr << str << " : " << strerror(errno) << endl;
    exit(1);
}

int ServerCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    cout <<  "do some server cron jobs..." << " Timer id = " << id << endl;
    return 5000;
}

void ClientDataHandler(struct aeEventLoop *eventloop, int clientfd, void *clientData, int mask) {
    char buf[BUFFER];
    memset(buf, 0, BUFFER);
    if (recv(clientfd, buf, BUFFER, 0) < 0) {
        Die("recv failed");
    }
    if (send(clientfd, buf, strlen(buf), 0) < 0) {
        Die("send failed");
    }

    if (strncmp(buf, "exit", 4) == 0) {
        cout << "bye: clientfd" << endl;
        aeDeleteFileEvent(eventloop, clientfd, AE_READABLE);
        close(clientfd);
    }

    if (strncmp(buf, "stop", 4) == 0) {
        cout << "stop server" << endl;
        aeDeleteFileEvent(eventloop, clientfd, AE_READABLE);
        close(clientfd);
        aeStop(eventloop);
    }
}

void NewClientHandler(struct aeEventLoop *eventLoop, int listen_sock, void *clientData, int mask) {
    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);
    int clientfd = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len);
    if (clientfd < 0) {
        Die("accept failed");
    }

    cout << "accept: " << clientfd << endl;

    if (aeCreateFileEvent(eventLoop, clientfd, AE_READABLE, ClientDataHandler, NULL) == AE_ERR) {
        Die("aeCreateFileEvent in NewClientHandler failed");
    }
}

int main(int argc, char *argv[]) {
    struct sockaddr_in server_addr;
    aeEventLoop *eventloop;

    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0) {
        Die("socket failed");
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(9090);

    if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        Die("bind failed");
    }

    if (listen(listen_sock, LISTEN_BACKLOG) < 0) {
        Die("listen failed");
    }

    eventloop = aeCreateEventLoop(MAX_CLIENTS);
    if (eventloop == NULL) {
        Die("aeCreateEventLoop failed");
    }
    if (aeCreateFileEvent(eventloop, listen_sock, AE_READABLE, NewClientHandler, NULL) == AE_ERR) {
        Die("aeCreateFileEvent in main failed");
    }

    long long timer_id = aeCreateTimeEvent(eventloop, 2000, ServerCron, NULL, NULL);
    cout << "Create Timer " << timer_id << endl;

    aeMain(eventloop);

    aeDeleteTimeEvent(eventloop, timer_id);
    aeDeleteFileEvent(eventloop, listen_sock, AE_READABLE);
    aeDeleteEventLoop(eventloop);
}
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
#include "ae.h"

using namespace std;

#define MAX_CLIENTS 1024
#define LISTEN_BACKLOG 10
#define BUFFER 10240

void Die(const char *str) {
    cerr << str << " : " << strerror(errno) << endl;
    exit(1);
}

int ServerCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    cout <<  "do some server cron jobs..." << " Timer id = " << id << endl;
    return 5000;
}

void ClientDataHandler(struct aeEventLoop *eventloop, int clientfd, void *clientData, int mask) {
    char buf[BUFFER];
    memset(buf, 0, BUFFER);
    if (recv(clientfd, buf, BUFFER, 0) < 0) {
        Die("recv failed");
    }
    if (send(clientfd, buf, strlen(buf), 0) < 0) {
        Die("send failed");
    }

    if (strncmp(buf, "exit", 4) == 0) {
        cout << "bye: clientfd" << endl;
        aeDeleteFileEvent(eventloop, clientfd, AE_READABLE);
        close(clientfd);
    }

    if (strncmp(buf, "stop", 4) == 0) {
        cout << "stop server" << endl;
        aeDeleteFileEvent(eventloop, clientfd, AE_READABLE);
        close(clientfd);
        aeStop(eventloop);
    }
}

void NewClientHandler(struct aeEventLoop *eventLoop, int listen_sock, void *clientData, int mask) {
    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);
    int clientfd = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len);
    if (clientfd < 0) {
        Die("accept failed");
    }

    cout << "accept: " << clientfd << endl;

    if (aeCreateFileEvent(eventLoop, clientfd, AE_READABLE, ClientDataHandler, NULL) == AE_ERR) {
        Die("aeCreateFileEvent in NewClientHandler failed");
    }
}

int main(int argc, char *argv[]) {
    struct sockaddr_in server_addr;
    aeEventLoop *eventloop;

    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0) {
        Die("socket failed");
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(9090);

    if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        Die("bind failed");
    }

    if (listen(listen_sock, LISTEN_BACKLOG) < 0) {
        Die("listen failed");
    }

    eventloop = aeCreateEventLoop(MAX_CLIENTS);
    if (eventloop == NULL) {
        Die("aeCreateEventLoop failed");
    }
    if (aeCreateFileEvent(eventloop, listen_sock, AE_READABLE, NewClientHandler, NULL) == AE_ERR) {
        Die("aeCreateFileEvent in main failed");
    }

    long long timer_id = aeCreateTimeEvent(eventloop, 2000, ServerCron, NULL, NULL);
    cout << "Create Timer " << timer_id << endl;

    aeMain(eventloop);

    aeDeleteTimeEvent(eventloop, timer_id);
    aeDeleteFileEvent(eventloop, listen_sock, AE_READABLE);
    aeDeleteEventLoop(eventloop);
}