前言
- 前言
- 宏观梳理
- 启动过程
- 网络层
- 协议层
- 业务层
- 在保存到dict 的过程中,数据的形态也一直在变化
- 定义新的数据类型
- 小结
参考《Apache Kafka源码分析》——server服务端网络开发的基本套路
宏观梳理
整个轴线是redisServer 初始化并启动eventloop, eventLoop 创建redisClient 及驱动processCommand 方法进而 执行redisCommand 向 dict 中保存数据
本文 以一个SET KEY VALUE 来分析redis的 启动和保存流程
启动过程
redis.c
int main(int argc, char **argv) {
...
// 初始化服务器
initServerConfig();
...
// 将服务器设置为守护进程
if (server.daemonize) daemonize();
// 创建并初始化服务器数据结构
initServer();
...
// 运行事件处理器,一直到服务器关闭为止
aeSetBeforeSleepProc(server.el,beforeSleep);
aeMain(server.el);
// 服务器关闭,停止事件循环
aeDeleteEventLoop(server.el);
return 0
}
网络层
Redis的网络监听没有采用libevent等,而是自己实现了一套简单的机遇event驱动的API,具体见ae.c。事件处理器的主循环
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
// 如果有需要在事件处理前执行的函数,那么运行它
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
// 开始处理事件
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}
Redis 中的事件循环
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
struct timeval tv, *tvp;
...
// 获取最近的时间事件
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
shortest = aeSearchNearestTimer(eventLoop);
if (shortest) {
// 如果时间事件存在的话,那么根据最近可执行时间事件和现在时间的时间差来决定文件事件的阻塞时间
// 计算距今最近的时间事件还要多久才能达到,并将该时间距保存在 tv 结构中
aeGetTime(&now_sec, &now_ms);
} else {
// 执行到这一步,说明没有时间事件,那么根据 AE_DONT_WAIT 是否设置来决定是否阻塞,以及阻塞的时间长度
}
// 处理文件事件,阻塞时间由 tvp 决定
// 类似于 java nio 中的select
numevents = aeApiPoll(eventLoop, tvp);
for (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;
// 读事件
if (fe->mask & mask & AE_READABLE) {
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);
}
}
// 执行时间事件
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop);
}
这个event loop的逻辑可不孤单,netty中也有类似的EventLoop 中的 Loop 到底是什么?
Redis 中会处理两种事件:时间事件和文件事件。在每个事件循环中 Redis 都会先处理文件事件,然后再处理时间事件直到整个循环停止。 aeApiPoll 可看做文件事件的生产者(还有一部分文件事件来自accept等),processEvents 和 processTimeEvents 作为 Redis 中发生事件的消费者,每次都会从“事件池”(aeEventLoop的几个列表字段)中拉去待处理的事件进行消费。