Redis的网络模型是基于单线程的事件驱动机制实现的,使用了 Reactor 模式。它主要通过一个事件循环来处理网络I/O和文件I/O。Redis的高性能很大程度上依赖于这个轻量级的网络模型,该模型允许它在单线程中高效地处理大量并发连接。
核心数据结构和组件
1. 事件循环(Event Loop)
Redis使用aeEventLoop结构体来管理事件循环。事件循环本质上是一个无限循环,它处理所有注册的事件(文件事件和时间事件)。
c
typedef struct aeEventLoop {
int maxfd; /* highest file descriptor currently registered */
int setsize; /* max number of file descriptors tracked */
long long timeEventNextId;
aeFileEvent *events; /* Registered events */
aeFiredEvent *fired; /* Fired events */
aeTimeEvent *timeEventHead;
int stop;
void *apidata; /* This is used for polling API specific data */
aeBeforeSleepProc *beforesleep;
aeBeforeSleepProc *aftersleep;
} aeEventLoop;
2. 文件事件(File Event)
文件事件表示对文件描述符的读写操作。
c
typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE) */
aeFileProc *rfileProc;
aeFileProc *wfileProc;
void *clientData;
} aeFileEvent;
3. 时间事件(Time Event)
时间事件用于处理定时任务。
c
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;
核心函数和流程
1. 创建和初始化事件循环
c
aeEventLoop *aeCreateEventLoop(int setsize) {
aeEventLoop *eventLoop;
int i;
if ((eventLoop = malloc(sizeof(*eventLoop))) == NULL) goto err;
eventLoop->events = malloc(sizeof(aeFileEvent) * setsize);
eventLoop->fired = malloc(sizeof(aeFiredEvent) * setsize);
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
eventLoop->setsize = setsize;
eventLoop->timeEventHead = NULL;
eventLoop->timeEventNextId = 0;
eventLoop->stop = 0;
eventLoop->maxfd = -1;
eventLoop->beforesleep = NULL;
eventLoop->aftersleep = NULL;
if (aeApiCreate(eventLoop) == -1) goto err;
for (i = 0; i < setsize; i++)
eventLoop->events[i].mask = AE_NONE;
return eventLoop;
err:
if (eventLoop) {
free(eventLoop->events);
free(eventLoop->fired);
free(eventLoop);
}
return NULL;
}
2. 注册文件事件
c
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData) {
if (fd >= eventLoop->setsize) return AE_ERR;
aeFileEvent *fe = &eventLoop->events[fd];
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
return AE_ERR;
fe->mask |= mask;
if (mask & AE_READABLE) fe->rfileProc = proc;
if (mask & AE_WRITABLE) fe->wfileProc = proc;
fe->clientData = clientData;
if (fd > eventLoop->maxfd)
eventLoop->maxfd = fd;
return AE_OK;
}
3. 事件循环处理函数
c
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
if (eventLoop->aftersleep != NULL)
eventLoop->aftersleep(eventLoop);
}
}
int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
int processed = 0, numevents;
if (!(flags & AE_TIME_EVENTS)) return 0;
processed += processTimeEvents(eventLoop);
if (flags & AE_FILE_EVENTS) {
numevents = aeApiPoll(eventLoop, &tvp);
for (int j = 0; j < numevents; j++) {
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
int fd = eventLoop->fired[j].fd;
int rfired = 0;
if (fe->mask & mask & AE_READABLE) {
rfired = 1;
fe->rfileProc(eventLoop, fd, fe->clientData, mask);
}
if (fe->mask & mask & AE_WRITABLE) {
if (!rfired || fe->wfileProc != fe->rfileProc)
fe->wfileProc(eventLoop, fd, fe->clientData, mask);
}
processed++;
}
}
return processed;
}
4. 处理时间事件
时间事件主要用于定时任务,通过检查当前时间和事件触发时间来决定是否执行:
c
static int processTimeEvents(aeEventLoop *eventLoop) {
int processed = 0;
aeTimeEvent *te;
long long maxId;
time_t now = time(NULL);
te = eventLoop->timeEventHead;
while (te) {
long now_sec, now_ms;
long long id;
aeGetTime(&now_sec, &now_ms);
if (te->when_sec > now_sec || (te->when_sec == now_sec && te->when_ms > now_ms)) {
te = te->next;
continue;
}
id = te->id;
int retval = te->timeProc(eventLoop, id, te->clientData);
processed++;
if (retval != AE_NOMORE) {
te->when_sec = now_sec + retval / 1000;
te->when_ms = now_ms + retval % 1000;
if (te->when_ms >= 1000) {
te->when_sec++;
te->when_ms -= 1000;
}
} else {
aeDeleteTimeEvent(eventLoop, id);
}
te = eventLoop->timeEventHead;
}
return processed;
}
示例使用
以下是一个简单的示例,展示了如何使用Redis的网络模型处理文件事件:
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ae.h"
void fileEventProc(aeEventLoop *eventLoop, int fd, void *clientData, int mask) {
char buf[128];
int n = read(fd, buf, sizeof(buf));
if (n > 0) {
buf[n] = '\0';
printf("Read from fd %d: %s\n", fd, buf);
} else if (n == 0) {
printf("EOF on fd %d\n", fd);
aeDeleteFileEvent(eventLoop, fd, AE_READABLE);
close(fd);
} else {
perror("read");
}
}
int main(int argc, char **argv) {
aeEventLoop *eventLoop = aeCreateEventLoop(1024);
if (aeCreateFileEvent(eventLoop, STDIN_FILENO, AE_READABLE, fileEventProc, NULL) == AE_ERR) {
fprintf(stderr, "Could not create file event.\n");
return 1;
}
aeMain(eventLoop);
aeDeleteEventLoop(eventLoop);
return 0;
}
总结
Redis的网络模型是基于单线程事件驱动机制的,使用 Reactor 模式进行事件处理。这种模型允许 Redis 在单线程环境下高效地处理大量并发连接,通过注册、处理文件事件和时间事件实现高效的 I/O 操作。这种设计不仅简化了编程模型,还显著提升了系统的性能和可维护性。