Redis的高性能很大程度上依赖其事件驱动模型和高效的网络层实现。基于单线程的事件循环,Redis能够处理大量并发连接而无需多线程开销。本篇将深入剖析Redis的事件循环框架(ae.c
)和网络处理机制(networking.c
),揭示其如何实现高并发。
Redis的事件循环基于ae.c
,支持两种事件:
- 文件事件(File Event):处理客户端socket的读写。
- 时间事件(Time Event):执行定时任务(如过期键清理)。
底层I/O多路复用机制根据系统选择:
- Linux:
epoll
(默认)。 - BSD/macOS:
kqueue
。 - Solaris:
evport
。 - 其他:
select
。
代码片段(ae.h
):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| typedef struct aeEventLoop {
int maxfd; // 最大文件描述符
aeFileEvent *events; // 文件事件数组
aeFiredEvent *fired; // 已触发事件数组
aeTimeEvent *timeEventHead; // 时间事件链表
int stop; // 停止标志
void *apidata; // 多路复用API数据(如epoll)
} aeEventLoop;
typedef struct aeFileEvent {
int mask; // 事件类型(AE_READABLE | AE_WRITABLE)
aeFileProc *rfileProc; // 读回调
aeFileProc *wfileProc; // 写回调
void *clientData; // 客户端数据
} aeFileEvent;
|
硬核解析:
events
:文件事件表,索引为fd。fired
:记录触发的事件。timeEventHead
:单链表存储定时任务。
Mermaid结构图:
代码片段(ae.c
):
1
2
3
4
5
6
7
8
9
10
11
| int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData) {
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;
}
|
硬核解析:
aeApiAddEvent()
:调用底层API(如epoll_ctl
)注册fd。- 事件类型:
AE_READABLE
(可读)、AE_WRITABLE
(可写)。 maxfd
:便于动态调整事件表。
代码片段(ae.c
):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
int processed = 0;
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
int numevents = aeApiPoll(eventLoop, eventLoop->fired); // I/O多路复用
for (int j = 0; j < numevents; j++) {
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
if (mask & AE_READABLE) fe->rfileProc(eventLoop, fe->fd, fe->clientData, mask);
if (mask & AE_WRITABLE) fe->wfileProc(eventLoop, fe->fd, fe->clientData, mask);
processed++;
}
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop); // 处理时间事件
return processed;
}
|
硬核解析:
aeApiPoll()
:调用epoll_wait
获取就绪事件。- 回调执行:根据
mask
调用读/写处理函数。 - 时间事件:检查链表,执行到期任务。
Mermaid事件循环流程:
代码片段(networking.c
):
1
2
3
4
5
| void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
int cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
if (cfd == ANET_ERR) return;
createClient(cfd); // 创建客户端
}
|
硬核解析:
anetTcpAccept()
:封装accept()
,返回客户端fd。createClient()
:初始化client
结构体,注册读事件。
- 非阻塞I/O:通过
epoll
避免轮询阻塞。 - 事件分发:单线程按序处理,避免锁竞争。
- 内存操作:无需线程同步,效率极高。
- 收获:理解Redis事件驱动与网络层实现。
- 调试技巧:用
strace
跟踪epoll_wait
调用,或用gdb
打印eventLoop->fired
。 - 下一步:探索内存管理和持久化。