文档章节

redis源码阅读-事件机制

l
 liamQuan
发布于 2015/11/28 20:24
字数 1156
阅读 31
收藏 0

概述

redis的事件机制主要包括两类事件:文件事件(网络io事件)和定时器事件。redis的事件机制归根结底就是利用了epoll_wait来实现网络IO事件和定时器事件,通过epoll_wait的events参数监听了一系列网络IO事件,通过epoll_wait的timeout参数监听了定时器事件,每次从epoll_wait返回,或者是IO事件被触发,或者是定时器事件被触发

数据结构

核心数据结构如下:

typedef struct aeEventLoop {
    int maxfd;   /* highest file descriptor currently registered */
    int setsize; /* max number of file descriptors tracked */
    long long timeEventNextId;
    time_t lastTime;     /* Used to detect system clock skew */
    aeFileEvent *events; /* Registered events */
    aeFiredEvent *fired; /* Fired events */
    aeTimeEvent *timeEventHead;
    int stop;
    void *apidata; /* This is used for polling API specific data */
    aeBeforeSleepProc *beforesleep;
} aeEventLoop;

其中,maxfd表示已监控的fd的最大值;setsize表示能够监控的fd的最大数量;timeEventNextId表示下一个定时器timer的id,id从0开始,每创建一个timer,id加1;lastTime用于检测系统事件的偏差;events是已经注册了的文件事件列表;fired是已经触发了的文件事件列表;timeEventHead是定时器列表;stop是停止标志,若其被设置,则退出eventloop;apidata中存储epoll fd和事件列表;beforesleep是阻塞的等待下一次事件发生之前要执行的函数。

整体数据结构如下:

文件事件

文件事件数据结构如下:

typedef struct aeFileEvent {
    int mask; /* one of AE_(READABLE|WRITABLE) */
    aeFileProc *rfileProc;
    aeFileProc *wfileProc;
    void *clientData;
} aeFileEvent;

其中,mask表示事件掩码,读或者写;rfileProc表示读事件处理函数;wfileProc表示写事件处理函数;clientData是传递给rfileProc和wfileProc的数据

定时器事件

定时器事件数据结构如下:

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;

其中,id是定时器id;when_sec和when_ms是定时器下次触发的时间;timeProc是定时器处理函数;finalizerProc是删除定时器时调用的函数;clientData是传递给timeProc和finalizerProc的参数;next是指向下一个定时器的指针。

api

aeEventLoop *aeCreateEventLoop(int setsize);            //创建事件循环体结构
void aeDeleteEventLoop(aeEventLoop *eventLoop);         //删除事件循环体结构
void aeStop(aeEventLoop *eventLoop);                    //结束事件循环

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 
        aeFileProc *proc, void *clientData);                         //创建文件事件
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);    //删除文件事件
int aeGetFileEvents(aeEventLoop *eventLoop, int fd);                 //获取文件事件掩码

long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
        aeTimeProc *proc, void *clientData,
        aeEventFinalizerProc *finalizerProc);                    //创建定时器事件
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);     //删除定时器事件

int aeProcessEvents(aeEventLoop *eventLoop, int flags);          //事件处理主函数,先处理文件事件,在处理定时器事件
int aeWait(int fd, int mask, long long milliseconds);            //阻塞等待fd上面的事件发生
void aeMain(aeEventLoop *eventLoop);                             //事件主循环,直到调用了aeStop结束循环
char *aeGetApiName(void);                                        //获取具体平台的多路io机制名
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);    //设置beforesleep函数

应用

#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
#include "ae.h"

using namespace std;

#define MAX_CLIENTS 1024
#define LISTEN_BACKLOG 10
#define BUFFER 10240

void Die(const char *str) {
    cerr << str << " : " << strerror(errno) << endl;
    exit(1);
}

int ServerCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    cout <<  "do some server cron jobs..." << " Timer id = " << id << endl;
    return 5000;
}

void ClientDataHandler(struct aeEventLoop *eventloop, int clientfd, void *clientData, int mask) {
    char buf[BUFFER];
    memset(buf, 0, BUFFER);
    if (recv(clientfd, buf, BUFFER, 0) < 0) {
        Die("recv failed");
    }
    if (send(clientfd, buf, strlen(buf), 0) < 0) {
        Die("send failed");
    }

    if (strncmp(buf, "exit", 4) == 0) {
        cout << "bye: clientfd" << endl;
        aeDeleteFileEvent(eventloop, clientfd, AE_READABLE);
        close(clientfd);
    }

    if (strncmp(buf, "stop", 4) == 0) {
        cout << "stop server" << endl;
        aeDeleteFileEvent(eventloop, clientfd, AE_READABLE);
        close(clientfd);
        aeStop(eventloop);
    }
}

void NewClientHandler(struct aeEventLoop *eventLoop, int listen_sock, void *clientData, int mask) {
    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);
    int clientfd = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len);
    if (clientfd < 0) {
        Die("accept failed");
    }

    cout << "accept: " << clientfd << endl;

    if (aeCreateFileEvent(eventLoop, clientfd, AE_READABLE, ClientDataHandler, NULL) == AE_ERR) {
        Die("aeCreateFileEvent in NewClientHandler failed");
    }
}

int main(int argc, char *argv[]) {
    struct sockaddr_in server_addr;
    aeEventLoop *eventloop;

    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0) {
        Die("socket failed");
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(9090);

    if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        Die("bind failed");
    }

    if (listen(listen_sock, LISTEN_BACKLOG) < 0) {
        Die("listen failed");
    }

    eventloop = aeCreateEventLoop(MAX_CLIENTS);
    if (eventloop == NULL) {
        Die("aeCreateEventLoop failed");
    }
    if (aeCreateFileEvent(eventloop, listen_sock, AE_READABLE, NewClientHandler, NULL) == AE_ERR) {
        Die("aeCreateFileEvent in main failed");
    }

    long long timer_id = aeCreateTimeEvent(eventloop, 2000, ServerCron, NULL, NULL);
    cout << "Create Timer " << timer_id << endl;

    aeMain(eventloop);

    aeDeleteTimeEvent(eventloop, timer_id);
    aeDeleteFileEvent(eventloop, listen_sock, AE_READABLE);
    aeDeleteEventLoop(eventloop);
}



© 著作权归作者所有

l
粉丝 0
博文 4
码字总数 4672
作品 0
昌平
私信 提问
《Redis设计与实现(第二版)》

《Redis 设计与实现》一书全面而完整地讲解了 Redis 的内部运行机制, 对 Redis 的大多数单机功能以及所有多机功能的实现原理进行了介绍, 展示了这些功能的核心数据结构以及关键的算法思想。...

ddddd8
2017/12/21
0
0
Redis 开源文档《Redis设计与实现》

Redis是运用比较广泛的NoSQL产品之一,目前的稳定版本是2.6.10,包括Github、Instagram、Blizzard、新浪微博等都在产品中大量使用了Redis。其代码基于BSD协议开源,整个项目代码量只有2万多行...

桂荣
2013/03/14
7.1K
21
Redis缓存失效机制

Redis缓存失效的故事要从EXPIRE这个命令说起,EXPIRE允许用户为某个key指定超时时间,当超过这个时间之后key对应的值会被清除,这篇文章主要在分析Redis源码的基础上站在Redis设计者的角度去...

Float_Luuu
2016/05/22
3.8K
1
Redis源码阅读(二)高可用设计——复制

Redis源码阅读(二)高可用设计-复制 复制的概念:Redis的复制简单理解就是一个Redis服务器从另一台Redis服务器复制所有的Redis数据库数据,能保持两台Redis服务器的数据库数据一致。 使用场...

gogo一
2018/08/16
0
0
Redis源码剖析--源码结构解析

如何阅读 Redis 源码? 找工作那会儿,看了黄建宏老师的《Redis设计与实现》,对redis的部分实现有了一个简明的认识。在面试过程中,redis确实成为了面试官考核我的一个亮点,恰好以后的工作...

mickelfeng
2018/12/20
61
0

没有更多内容

加载失败,请刷新页面

加载更多

PostgreSQL 11.3 locking

rudi
今天
5
0
Mybatis Plus sql注入器

一、继承AbstractMethod /** * @author beth * @data 2019-10-23 20:39 */public class DeleteAllMethod extends AbstractMethod { @Override public MappedStatement injectMap......

一个yuanbeth
今天
10
1
一次写shell脚本的经历记录——特殊字符惹的祸

本文首发于微信公众号“我的小碗汤”,扫码文末二维码即可关注,欢迎一起交流! redis在容器化的过程中,涉及到纵向扩pod实例cpu、内存以及redis实例的maxmemory值,statefulset管理的pod需要...

码农实战
今天
4
0
为什么阿里巴巴Java开发手册中不建议在循环体中使用+进行字符串拼接?

之前在阅读《阿里巴巴Java开发手册》时,发现有一条是关于循环体中字符串拼接的建议,具体内容如下: 那么我们首先来用例子来看看在循环体中用 + 或者用 StringBuilder 进行字符串拼接的效率...

武培轩
今天
8
0
队列-链式(c/c++实现)

队列是在线性表功能稍作修改形成的,在生活中排队是不能插队的吧,先排队先得到对待,慢来得排在最后面,这样来就形成了”先进先出“的队列。作用就是通过伟大的程序员来实现算法解决现实生活...

白客C
今天
81
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部