EventLoop

EventLoop

Overview

先給出Redis關于\color{red}{EventLoop}的定義(見下文代碼):
在ae.h中,定義了基本的數(shù)據(jù)模型和接口艘希,其實這個思想也貫穿了整個Redis的設計中硼身,Java是通過interface或者abstract class來達到抽象的方式,而C中覆享,往往通過一個頭文件佳遂,定義了基本接口,具體實現(xiàn)可以再不同文件中實現(xiàn)撒顿。

Redis對于EventLoop的處理主要集中在 ae.h/ae.c中丑罪,分為兩部分,一部分是對于文件(網(wǎng)絡)事件的處理凤壁,另一部分是對于時間事件的處理(定時任務和周期性任務)吩屹。
對于兩種不同的時間類型,Redis使用了不同的設計客扎。

文件事件主要是網(wǎng)絡socket的處理祟峦,基于不同的操作系統(tǒng)或操作系統(tǒng)版本之間,設置的網(wǎng)絡模型不同徙鱼,需要有不同的交互方式宅楞。這里,Redis的實現(xiàn)方式為抽象出與底層交互的統(tǒng)一接口\color{red}{見下文代碼處}袱吆,通過實現(xiàn)相應接口和加載(初始化時厌衙,會根據(jù)系統(tǒng)不同加載不同的文件,有點Java中SPI的思想)绞绒。從設計模式的角度講(以后會專門講這個婶希,筆者認為不應該糾結(jié)于設計模式本身,而是應該通過設計模式來加深對于OO的理解)蓬衡,類似于Adapter模式喻杈。

時間事件的處理分為兩類,定時任務周期性任務狰晚,兩種任務的不同處理之處在于筒饰,周期性任務執(zhí)行后,會重新添加到時間任務列表中(Redis通過一個單鏈表來實現(xiàn)時間任務隊列壁晒,每次輪詢這個鏈表來執(zhí)行到期的任務瓷们,這點筆者認為使用這種數(shù)據(jù)結(jié)構(gòu)來維護時間列表會不會更合理一些?)。不同于文件事件谬晕,時間事件的處理主要是通過傳入的回調(diào)函數(shù)執(zhí)行的碘裕。
aeEventLoop:

typedef struct aeEventLoop {
    // 目前已注冊的最大描述符
    int maxfd;   /* highest file descriptor currently registered */
    // 目前已追蹤的最大描述符
    int setsize; /* max number of file descriptors tracked */
    // 用于生成時間事件 id
    long long timeEventNextId;
    // 最后一次執(zhí)行時間事件的時間
    time_t lastTime;     /* Used to detect system clock skew */
    // 已注冊的文件事件
    aeFileEvent *events; /* Registered events */
    // 已就緒的文件事件
    aeFiredEvent *fired; /* Fired events */
    // 時間事件
    aeTimeEvent *timeEventHead;
    // 事件處理器的開關
    int stop;
    // 多路復用庫的私有數(shù)據(jù)
    void *apidata; /* This is used for polling API specific data */
    // 在處理事件前要執(zhí)行的函數(shù)
    aeBeforeSleepProc *beforesleep;
} aeEventLoop;

抽象出的接口如下:

/* Prototypes */
void aeMain(aeEventLoop *eventLoop);
int aeProcessEvents(aeEventLoop *eventLoop, int flags);

//Event Loop Related
aeEventLoop *aeCreateEventLoop(int setsize);
void aeDeleteEventLoop(aeEventLoop *eventLoop);
void aeStop(aeEventLoop *eventLoop);

//File Event Related
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);

//Time Event Related
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
        aeTimeProc *proc, void *clientData,
        aeEventFinalizerProc *finalizerProc);
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);

//Others
int aeWait(int fd, int mask, long long milliseconds);
char *aeGetApiName(void);
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);
int aeGetSetSize(aeEventLoop *eventLoop);
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);

文件事件

對于文件事件的處理,Redis得益于對事件的封裝上攒钳,Redis封裝的事件有以下幾種:

static int aeApiCreate(aeEventLoop *eventLoop);
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask)
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp)

static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask)
static int aeApiResize(aeEventLoop *eventLoop, int setsize);
static void aeApiFree(aeEventLoop *eventLoop);
static char *aeApiName(void)

對應關系為


具體怎樣運作的帮孔,放在下一節(jié)中詳細說明

時間事件

關于時間事件,Redis中主要運行的可能就是serverCron了

Interaction

本節(jié)內(nèi)容主要是以epoll為例夕玩,介紹Redis面向接口的一種設計的具體實現(xiàn)

Redis通過ae.c/ae.h中暴露出的接口和epoll(或其他socket類型的api)交互你弦,溝通的結(jié)構(gòu)為eventloop中的apidata

typedef struct aeEventLoop {
....
....
    // 多路復用庫的私有數(shù)據(jù)
    <font color=red><b>void *apidata; /* This is used for polling API specific data */ </b></font>

....

} aeEventLoop;

apiData 在 epoll.c 中的對應數(shù)據(jù)結(jié)構(gòu)如下:

typedef struct aeApiState {
    // epoll_event 實例描述符
    int epfd;

    // 事件槽
    struct epoll_event *events;

} aeApiState;

初始化時,調(diào)用int aeApiCreate(aeEventLoop *eventLoop)對eventLoop所相關的進行init操作燎孟, 如下(只記錄正常邏輯)

static int aeApiCreate(aeEventLoop *eventLoop) {

    aeApiState *state = zmalloc(sizeof(aeApiState));
    // 初始化事件槽空間
    state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);
    // 創(chuàng)建 epoll 實例, 調(diào)用epoll接口
    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */

    // 賦值給 eventLoop
    eventLoop->apidata = state;
    return 0;
}

同時禽作,函數(shù)調(diào)用方(callee)為aeEventLoop的初始化函數(shù):

aeEventLoop *aeCreateEventLoop(int setsize) {
    aeEventLoop *eventLoop;
    int i;
    // 創(chuàng)建事件狀態(tài)結(jié)構(gòu)
    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;

    // 初始化文件事件結(jié)構(gòu)和已就緒文件事件結(jié)構(gòu)數(shù)組
   ...

    // 初始化時間事件結(jié)構(gòu)
    ...

  /** 調(diào)用API**/
    if (aeApiCreate(eventLoop) == -1) goto err;

    /* Events with mask == AE_NONE are not set. So let's initialize the
     * vector with it. */
    // 初始化監(jiān)聽事件
    for (i = 0; i < setsize; i++)
        eventLoop->events[i].mask = AE_NONE;

    // 返回事件循環(huán)
    return eventLoop;
err:
    if (eventLoop) {
        zfree(eventLoop->events);
        zfree(eventLoop->fired);
        zfree(eventLoop);
    }
    return NULL;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市揩页,隨后出現(xiàn)的幾起案子旷偿,更是在濱河造成了極大的恐慌,老刑警劉巖爆侣,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萍程,死亡現(xiàn)場離奇詭異,居然都是意外死亡兔仰,警方通過查閱死者的電腦和手機茫负,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乎赴,“玉大人忍法,你說我怎么就攤上這事¢藕穑” “怎么了饿序?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長羹蚣。 經(jīng)常有香客問我原探,道長,這世上最難降的妖魔是什么顽素? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任咽弦,我火速辦了婚禮,結(jié)果婚禮上胁出,老公的妹妹穿的比我還像新娘离唬。我一直安慰自己,他們只是感情好划鸽,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布缤沦。 她就那樣靜靜地躺著,像睡著了一般彬伦。 火紅的嫁衣襯著肌膚如雪蹬竖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天丈冬,我揣著相機與錄音嘱函,去河邊找鬼。 笑死埂蕊,一個胖子當著我的面吹牛往弓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蓄氧,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼函似,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了喉童?” 一聲冷哼從身側(cè)響起撇寞,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎堂氯,沒想到半個月后蔑担,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡咽白,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年啤握,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晶框。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡排抬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出三妈,到底是詐尸還是另有隱情畜埋,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布畴蒲,位于F島的核電站悠鞍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏模燥。R本人自食惡果不足惜咖祭,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔫骂。 院中可真熱鬧么翰,春花似錦、人聲如沸辽旋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至码耐,卻和暖如春追迟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骚腥。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工敦间, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人束铭。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓廓块,卻偏偏與公主長得像,于是被迫代替她去往敵國和親契沫。 傳聞我的和親對象是個殘疾皇子带猴,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354