在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Linux/ event 模塊
示例: hello handler 模塊
什么是 Nginx
handler 模塊的掛載
Nginx 特點
handler 模塊簡介
初探 Nginx 架構(gòu)
Nginx 的模塊化體系結(jié)構(gòu)
更多 handler 模塊示例分析
Nginx 基礎(chǔ)概念
upstream 模塊簡介
Nginx 的請求處理
過濾模塊簡介
基本數(shù)據(jù)結(jié)構(gòu)
模塊的基本結(jié)構(gòu)
負(fù)載均衡模塊
過濾模塊的分析
core 模塊
handler 模塊的基本結(jié)構(gòu)
Nginx 的配置系統(tǒng)
handler 的編寫步驟
handler 模塊的編譯和使用
event 模塊

event 模塊

event 的類型和功能

Nginx 是以 event(事件)處理模型為基礎(chǔ)的模塊。它為了支持跨平臺,抽象出了 event 模塊。它支持的 event 處理類型有:AIO(異步IO),/dev/poll(Solaris 和 Unix 特有),epoll(Linux 特有),eventport(Solaris 10 特有),kqueue(BSD 特有),poll,rtsig(實時信號),select 等。

event 模塊的主要功能就是,監(jiān)聽 accept 后建立的連接,對讀寫事件進行添加刪除。事件處理模型和 Nginx 的非阻塞 IO 模型結(jié)合在一起使用。當(dāng) IO 可讀可寫的時候,相應(yīng)的讀寫事件就會被喚醒,此時就會去處理事件的回調(diào)函數(shù)。

特別對于 Linux,Nginx 大部分 event 采用 epoll EPOLLET(邊沿觸發(fā))的方法來觸發(fā)事件,只有 listen 端口的讀事件是 EPOLLLT(水平觸發(fā))。對于邊沿觸發(fā),如果出現(xiàn)了可讀事件,必須及時處理,否則可能會出現(xiàn)讀事件不再觸發(fā),連接餓死的情況。

typedef struct {
        /* 添加刪除事件 */
        ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
        ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

        ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
        ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

        /* 添加刪除連接,會同時監(jiān)聽讀寫事件 */
        ngx_int_t  (*add_conn)(ngx_connection_t *c);
        ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

        ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
        /* 處理事件的函數(shù) */
        ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                                   ngx_uint_t flags);

        ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
        void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;

上述是 event 處理抽象出來的關(guān)鍵結(jié)構(gòu)體,可以看到,每個 event 處理模型,都需要實現(xiàn)部分功能。最關(guān)鍵的是 add 和 del 功能,就是最基本的添加和刪除事件的函數(shù)。

accept 鎖

Nginx 是多進程程序,80 端口是各進程所共享的,多進程同時 listen 80 端口,勢必會產(chǎn)生競爭,也產(chǎn)生了所謂的“驚群”效應(yīng)。當(dāng)內(nèi)核 accept 一個連接時,會喚醒所有等待中的進程,但實際上只有一個進程能獲取連接,其他的進程都是被無效喚醒的。所以 Nginx 采用了自有的一套 accept 加鎖機制,避免多個進程同時調(diào)用 accept。Nginx 多進程的鎖在底層默認(rèn)是通過 CPU 自旋鎖來實現(xiàn)。如果操作系統(tǒng)不支持自旋鎖,就采用文件鎖。

Nginx 事件處理的入口函數(shù)是 ngx_process_events_and_timers(),下面是部分代碼,可以看到其加鎖的過程:

if (ngx_use_accept_mutex) {
        if (ngx_accept_disabled > 0) {
                ngx_accept_disabled--;

        } else {
                if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                        return;
                }

                if (ngx_accept_mutex_held) {
                        flags |= NGX_POST_EVENTS;

                } else {
                        if (timer == NGX_TIMER_INFINITE
                                || timer > ngx_accept_mutex_delay)
                        {
                                timer = ngx_accept_mutex_delay;
                        }
                }
        }
}

在 ngx_trylock_accept_mutex()函數(shù)里面,如果拿到了鎖,Nginx 會把 listen 的端口讀事件加入 event 處理,該進程在有新連接進來時就可以進行 accept 了。注意 accept 操作是一個普通的讀事件。下面的代碼說明了這點:

(void) ngx_process_events(cycle, timer, flags);

if (ngx_posted_accept_events) {
        ngx_event_process_posted(cycle, &ngx_posted_accept_events);
}

if (ngx_accept_mutex_held) {
        ngx_shmtx_unlock(&ngx_accept_mutex);
}

ngx_process_events()函數(shù)是所有事件處理的入口,它會遍歷所有的事件。搶到了 accept 鎖的進程跟一般進程稍微不同的是,它被加上了 NGX_POST_EVENTS 標(biāo)志,也就是說在 ngx_process_events() 函數(shù)里面只接受而不處理事件,并加入 post_events 的隊列里面。直到 ngx_accept_mutex 鎖去掉以后才去處理具體的事件。為什么這樣?因為 ngx_accept_mutex 是全局鎖,這樣做可以盡量減少該進程搶到鎖以后,從 accept 開始到結(jié)束的時間,以便其他進程繼續(xù)接收新的連接,提高吞吐量。

ngx_posted_accept_events 和 ngx_posted_events 就分別是 accept 延遲事件隊列和普通延遲事件隊列??梢钥吹?ngx_posted_accept_events 還是放到 ngx_accept_mutex 鎖里面處理的。該隊列里面處理的都是 accept 事件,它會一口氣把內(nèi)核 backlog 里等待的連接都 accept 進來,注冊到讀寫事件里。

而 ngx_posted_events 是普通的延遲事件隊列。一般情況下,什么樣的事件會放到這個普通延遲隊列里面呢?我的理解是,那些 CPU 耗時比較多的都可以放進去。因為 Nginx 事件處理都是根據(jù)觸發(fā)順序在一個大循環(huán)里依次處理的,因為 Nginx 一個進程同時只能處理一個事件,所以有些耗時多的事件會把后面所有事件的處理都耽擱了。

除了加鎖,Nginx 也對各進程的請求處理的均衡性作了優(yōu)化,也就是說,如果在負(fù)載高的時候,進程搶到的鎖過多,會導(dǎo)致這個進程被禁止接受請求一段時間。

比如,在 ngx_event_accept 函數(shù)中,有類似代碼:

ngx_accept_disabled = ngx_cycle->connection_n / 8
              - ngx_cycle->free_connection_n;

ngx_cycle->connection_n 是進程可以分配的連接總數(shù),ngx_cycle->free_connection_n 是空閑的進程數(shù)。上述等式說明了,當(dāng)前進程的空閑進程數(shù)小于 1/8 的話,就會被禁止 accept 一段時間。

定時器

Nginx 在需要用到超時的時候,都會用到定時器機制。比如,建立連接以后的那些讀寫超時。Nginx 使用紅黑樹來構(gòu)造定期器,紅黑樹是一種有序的二叉平衡樹,其查找插入和刪除的復(fù)雜度都為 O(logn),所以是一種比較理想的二叉樹。

定時器的機制就是,二叉樹的值是其超時時間,每次查找二叉樹的最小值,如果最小值已經(jīng)過期,就刪除該節(jié)點,然后繼續(xù)查找,直到所有超時節(jié)點都被刪除。