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