本节中我们重点介绍 initServer , 在下一节介绍事件处理循环 aeMain 。在 initServer 这个函数内 , Redis 做了这么三件重要的事情 。

文章插图
- 创建一个 epoll 对象
- 对配置的监听端口进行 listen
- 把 listen socket 让 epoll 给管理起来
//file: src/server.cvoid initServer() {// 2.1.1 创建 epollserver.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);// 2.1.2 绑定监听服务端口listenToPort(server.port,server.ipfd,&server.ipfd_count);// 2.1.3 注册 accept 事件处理器for (j = 0; j < server.ipfd_count; j++) {aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,acceptTcpHandler,NULL);}...}接下来我们分别来看 。2.1 创建 epoll 对象本小节的逻辑看起来貌似不短 , 但其实只是创建了一个 epoll 对象出来而已 。
创建 epoll 对象的逻辑在 aeCreateEventLoop 中 , 在创建完后 , Redis 将其保存在 redisServer 的 aeEventLoop 成员中 , 以备后续使用 。
struct redisServer {...aeEventLoop *el;}我们来看 aeCreateEventLoop 详细逻辑 。Redis 在操作系统提供的 epoll 对象基础上又封装了一个 eventLoop 出来 , 所以创建的时候是先申请和创建 eventLoop 。//file:src/ae.caeEventLoop *aeCreateEventLoop(int setsize) {aeEventLoop *eventLoop;eventLoop = zmalloc(sizeof(*eventLoop);//将来的各种回调事件就都会存在这里eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);......aeApiCreate(eventLoop);return eventLoop;}在 eventLoop 里 , 我们稍微注意一下 eventLoop->events , 将来在各种事件注册的时候都会保存到这个数组里 。//file:src/ae.htypedef struct aeEventLoop {......aeFileEvent *events; /* Registered events */}具体创建 epoll 的过程在 ae_epoll.c 文件下的 aeApiCreate 中 。在这里 , 真正调用了 epoll_create//file:src/ae_epoll.cstatic int aeApiCreate(aeEventLoop *eventLoop) {aeApiState *state = zmalloc(sizeof(aeApiState));state->epfd = epoll_create(1024);eventLoop->apidata = https://www.isolves.com/it/sjk/Redis/2022-05-11/state;return 0;}2.2 绑定监听服务端口我们再来看 Redis 中的 listen 过程 , 它在 listenToPort 函数中 。虽然调用链条很长 , 但其实主要就是执行了个简单 listen 而已 。//file: src/redis.cint listenToPort(int port, int *fds, int *count) {for (j = 0; j < server.bindaddr_count || j == 0; j++) {fds[*count] = anetTcpServer(server.neterr,port,NULL,server.tcp_backlog);}}Redis 是支持开启多个端口的 , 所以在 listenToPort 中我们看到是启用一个循环来调用 anetTcpServer 。在 anetTcpServer 中 , 逐步会展开调用 , 直到执行到 bind 和 listen 系统调用 。//file:src/anet.cint anetTcpServer(char *err, int port, char *bindaddr, int backlog){return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);}static int _anetTcpServer(......){// 设置端口重用anetSetReuseAddr(err,s)// 监听anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog)}static int anetListen(......) {bind(s,sa,len);listen(s, backlog);......}2.3 注册事件回调函数我们回头再看一下 initServer , 它调用 aeCreateEventLoop 创建了 epoll , 调用 listenToPort 进行了服务端口的 bind 和 listen 。接着就开始调用 aeCreateFileEvent 来注册一个 accept 事件处理器 。//file: src/server.cvoid initServer() {// 2.1.1 创建 epollserver.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);// 2.1.2 监听服务端口listenToPort(server.port,server.ipfd,&server.ipfd_count);// 2.1.3 注册 accept 事件处理器for (j = 0; j < server.ipfd_count; j++) {aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,acceptTcpHandler,NULL);}...}我们来注意看调用 aeCreateFileEvent 时传的重要参数是 acceptTcpHandler , 它表示将来在 listen socket 上有新用户连接到达的时候 , 该函数将被调用执行 。我们来看 aeCreateFileEvent 具体代码 。//file: src/ae.cint aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,aeFileProc *proc, void *clientData){// 取出一个文件事件结构aeFileEvent *fe = &eventLoop->events[fd];// 监听指定 fd 的指定事件aeApiAddEvent(eventLoop, fd, mask);// 设置文件事件类型 , 以及事件的处理器fe->mask |= mask;if (mask & AE_READABLE) fe->rfileProc = proc;if (mask & AE_WRITABLE) fe->wfileProc = proc;// 私有数据fe->clientData = https://www.isolves.com/it/sjk/Redis/2022-05-11/clientData;}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- |3款网红“卸妆油”深度测评:MAC温和不刺激,植村秀虽贵但真好用
- 红茶喝上火吗,秋天上火喝红茶好吗
- 对联谜语答案及解析?对联猜谜大全及答案简单的
- 《亚瑟王的荣耀》铭文解析
- 冬天喝祁门红茶,祁门红茶女人喝好吗
- 茶香门第红茶,红茶的产地
- 迪奥香水广告解析
- 解析:书房风水为何不宜向阳
- 详细解析:书房风水挂什么字画好
- 姜文《让子弹飞》深度影评?姜文评价让子弹飞
