Reactor模型簡介

服務器編程需要處理大量鏈接,reactor模型是一種高效的事件處理模型,常常用於處理這種問題,其核心是一個不斷查詢的循環,查詢多個可能發生事件的事件源

while(true)
{
    //查詢事件源是否有事件
}

爲了同時能夠處理大量鏈接,一般會使用多路複用I/O來監聽,比如epoll或select。

while(true)
{
    int ret = select(&read_fds, &write_fds, &exp_fds, NULL);
}

監聽到準備好的I/O事件之後就需要分別處理掉,也就是所謂的分發,畢竟讀任務,寫任務,異常都需要不同的處理。

while(true)
{
    int ret = select(&read_fds, &write_fds, &exp_fds, NULL);
    //封裝成任務,分發出去處理
}

比如什麼都不做,只是出現讀讀寫寫處理異常的形式

while(true)
{
    int ret = select(&read_fds, &write_fds, &exp_fds, NULL);
    for_each(fd in read_fds)
    {
        do_read(fd);
    }
    for_each(fd in write_fds)
    {
        do_write(fd);
    }
    for_each(fd in exp_fds)
    {
        do_excep(fd);
    }
}

通常在do_*函數裏面就幹自己想要處理的動作。
在每一次 while(true) 循環的最後會清理掉已經發生過事件的事件源,並重新註冊下一次需要監聽的事件源。因此在do_*函數裏面最後一件事情就是決定還要不要繼續監聽這次處理過的fd,也既是重新註冊回去。

while(true)
{
    int ret = select(&read_fds, &write_fds, &exp_fds, NULL);
    //封裝成分發任務,分發出去處理
    clear(&read_fds, &write_fds, &exp_fds);
    re_register();
}

用epoll的ET模式會省事兒很多,觸發一次之後就不再觸發,除非再次註冊。

while(true)
{
    int ret = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
    for(int i = 0; i < ret; ++i)
    {
        if(events[i].fd == listenfd)
        {
            //添加新進入的socket
        }
        if(events[i].events & EPOLLIN)
        {
            //讀數據,然後封裝成任務分發處理
        }
        else if(events[i].events & EPOLLOUT)
        {
            //寫數據
        }
    }
}

void process()
{
    //處理數據
    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, 0 );
    //or epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event );
    //or epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event );
}

單線程的reactor分發後的處理也在同一個線程中,需要處理比較迅速,絕對不能阻塞,否則整個事件循環就阻塞了,這是個悲劇。
多線程的reactor會將任務分發到不同的線程中去處理,最大化利用多核cpu。可以利用線程池來管理這些線程,線程池持有一個任務隊列,各個線程競爭的從任務隊列裏面領取任務來處理。在多個線程中調用epoll_ctl和epoll_wait是線程安全的。

更多學習:
C語言reactor模式參考libevent
python語言reactor模式參考twisted

參考資料:
《Linux高性能服務器編程》
http://www.cnblogs.com/hustcat/archive/2012/01/11/2319249.html

發佈了38 篇原創文章 · 獲贊 27 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章