作者:鱼红1993 | 来源:互联网 | 2018-07-13 10:04
前言Redis(REmoteDIctionaryServer)是一个由SalvatoreSanfilippo写的key-value存储系统。它有以下特点:总体结构Redis是一个单线程的服务器(除了写磁盘会开子进程,VM管理会开线程,先忽略这两块)所以,它的服务器启动流程非常清晰,看下图,不再
前言 Redis(REmoteDIctionaryServer)是一个由Salvatore Sanfilippo写的key-value存储系统。 它有以下特点: 总体结构 Redis是一个单线程的服务器(除了写磁盘会开子进程,VM管理会开线程,先忽略这两块) 所以,它的服务器启动流程非常清晰,看下图,不再
前言
Redis(REmote DIctionary Server)是一个由Salvatore Sanfilippo写的key-value存储系统。
它有以下特点:
总体结构
Redis是一个单线程的服务器(除了写磁盘会开子进程,VM管理会开线程,先忽略这两块)
所以,它的服务器启动流程非常清晰,看下图,不再赘述
事件循环
1. 数据结构
通过 aeEventLoop 结构来表示一个事件框架,美国服务器,分析如下:
1 typedef struct aeEventLoop {
timeEventNextId; aeFileEvent events[AE_SETSIZE]; aeFiredEvent fired[AE_SETSIZE]; aeTimeEvent *timeEventHead; stop;
aeBeforeSleepProc *beforesleep;
10 } aeEventLoop;
2. 事件函数分发流程
redis支持 epoll、kqueue、select 三种网络模型
网络框架的代码实现主要在以下几个文件中:
ae.c / ae.h // 网络框架
ae_epoll.c // epoll模型的实现
ae_kqueue.c // kqueue模型的实现
ae_select.c // select模型的实现
程序选择哪一种,网站空间,是在编译期确定的
1 1 file ae.c
#ifdef HAVE_EPOLL
#ifdef HAVE_KQUEUE
#include
框架函数非常简单,从初始化到结束,主要的函数就3个
aeCreateEventLoop、aeMain、aeDeleteEventLoop
其中,aeMain是事件循环的主体函数,它又会调用 aeProcessEvents函数
三个主体函数会调用 aeApiCreate、aeApiPool、aeApiFree三个接口函数进行处理
这三个接口函数又会映射到具体的某一种网络模型中,而这是在编译期确定下来的
具体如下图所示:
添加删除事件,由
aeCreateFileEvent、aeDeleteFileEvent函数完成,其函数分发流程如下图:
2. 服务端socket建立流程
1. 在服务器初始化时,建立侦听socket,并绑定IP、Port
支持 TCP连接 与本机的unixSocket连接
1
if (server.port != 0) {
2
server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr);
3
... ...
4 }
5
if (server.unixsocket != NULL) {
server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm);
8
... ...
9
}
2. 侦听socket绑定到事件句柄
1
if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
);
3
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
);
其中“acceptTcpHandler”、“acceptUnixHandler” 是事件回调函数
当新连接到达时,会触发进入这两个函数
3. 再来看看 accept***Handler 里做了什么
1 void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
2
int cport, cfd;
3
char cip[128];
4 REDIS_NOTUSED(el);
5 REDIS_NOTUSED(mask);
6 REDIS_NOTUSED(privdata);
cfd = anetTcpAccept(server.neterr, fd, cip, &cport);
10
if (cfd == AE_ERR) {
, server.neterr);
12
return;
13 }
, cip, cport);
}
acceptCommonHandler中做的事很简单,调用 CreateClient函数,创建新的redisClient对象
acceptCommonHandler(int fd) {
2
redisClient *c;
3
if ((c = createClient(fd)) == NULL) {
);
;
7 }
8 ... ...
9 }
在 createClient函数中,将新连接绑定到事件循环中
1 redisClient *createClient(int fd) {
2
redisClient *c = zmalloc(sizeof(redisClient));
3
c->bufpos = 0;
4
5 anetNonBlock(NULL,fd);
6 anetTcpNoDelay(NULL,fd);
7
if (aeCreateFileEvent(server.el,fd,AE_READABLE,
{
10
close(fd);
11
zfree(c);
12
return NULL;
13
}
当连接上有数据到达时,便会触发 readQueryFromClient函数,进行实际的网络数据读取与处理
3. Timer事件
Redis将所有Timer绑定到事件循环中进行处理
通过函数 aeCreateTimeEvent 创建新的Timer事件
每一帧事件循环中,通过processTimeEvents函数进行处理