本篇內(nèi)容主要講解“Nginx事件處理模塊怎么理解”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Nginx事件處理模塊怎么理解”吧!
站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到?jīng)芸h網(wǎng)站設(shè)計(jì)與涇縣網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站建設(shè)、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、空間域名、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋涇縣地區(qū)。
一、事件模塊主流程講解
事件模塊主要處理兩類事件:① 定時(shí)任務(wù) ② I/O事件。其中定時(shí)任務(wù)是指nginx通過(guò)ngx_add_timer添加的事件,原型函數(shù)如下:
ngx_event_timer.h
static ngx_inline void
ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
// 計(jì)算過(guò)期時(shí)間點(diǎn)
// 過(guò)期時(shí)間 = 現(xiàn)在時(shí)間 + 過(guò)期時(shí)間
key = ngx_current_msec + timer;
// 設(shè)置超時(shí)時(shí)間到
ev->timer.key = key;
// 添加的紅黑樹(shù)
ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);
}
// 并設(shè)置了全局變量方便在任何地方調(diào)用
#define ngx_add_timer ngx_event_add_timer在此列舉一個(gè)本文涉及的超時(shí)管理:
① 工作進(jìn)程在爭(zhēng)搶互斥鎖時(shí),因?yàn)橹挥幸粋€(gè)進(jìn)程能夠獲取鎖,如果其他進(jìn)程無(wú)法獲取鎖,不可能一直等待,所以設(shè)置accept_mutex_delay,超過(guò)了設(shè)定的事件就會(huì)發(fā)生超時(shí)處理,也就放棄爭(zhēng)搶鎖二選擇去釋放資源。
ngx_event_accept.c
void
ngx_event_accept(ngx_event_t *ev)
{
if (ngx_use_accept_mutex) {
// xxx
}else{
ngx_add_timer(ev, ecf->accept_mutex_delay);
}
}其次就是I/O事件了,很容易理解就是處理讀寫事件,那么I/O事件以epoll為例,先來(lái)看下nginx處理事件的主流程:
ngx_process_cycle.c
static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
for ( ;; ) {
// 不斷循環(huán)處理事件
ngx_process_events_and_timers(cycle);
}
}事件處理的入口在nginx進(jìn)程模型的代碼里面,工作進(jìn)程會(huì)一直循環(huán)取處理事件,其中的阻塞點(diǎn)就是epoll_wait,處理完一輪事件后又循環(huán)這個(gè)過(guò)程,直到master進(jìn)程發(fā)來(lái)關(guān)閉的信號(hào)。接下來(lái)就是真正的開(kāi)始處理事件的地方:
// 開(kāi)始處理事件
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
ngx_uint_t flags;
ngx_msec_t timer, delta;
// ngx_timer_resolution 用于決定使用何種超時(shí)策略
// 如果ngx_timer_resolution非零0,則設(shè)置timer未無(wú)限大
// 另外ngx_timer_resolution還有作用就是控制gettimeofday調(diào)用的頻率,不過(guò)在x86_64系統(tǒng)中影響已經(jīng)可忽略了
// 此時(shí)采用定時(shí)方案,在規(guī)定的時(shí)間,默認(rèn)500ms,對(duì)紅黑樹(shù)中的元素進(jìn)行一次掃描
// 并處理超時(shí)的節(jié)點(diǎn)
if (ngx_timer_resolution) {
// 先設(shè)置時(shí)間為無(wú)限待,后面會(huì)設(shè)置未500
timer = NGX_TIMER_INFINITE;
flags = 0;
} else {
// 如果ngx_timer_resolution為0
// 采用超時(shí)的方案,首先計(jì)算出最快超時(shí)的時(shí)間,然后等待這個(gè)時(shí)間段取處理超時(shí)的事件,
// 處理完超時(shí)任務(wù),再次計(jì)算下一次的超時(shí)時(shí)間,不斷地循環(huán)處理。
// 查詢紅黑樹(shù),返回超時(shí)時(shí)間最小的節(jié)點(diǎn),即最左邊的元素
/**
* while (node->left != sentinel) {
node = node->left;
}
*/
// nginx通過(guò)紅黑樹(shù)來(lái)維護(hù)所有的time節(jié)點(diǎn)
// 將超時(shí)檢測(cè)時(shí)間設(shè)置未最快發(fā)生超時(shí)的事件對(duì)象的超時(shí)時(shí)刻和當(dāng)前時(shí)刻的差
// timer = (ngx_msec_int_t) (node->key - ngx_current_msec);
timer = ngx_event_find_timer();
flags = NGX_UPDATE_TIME;
#if (NGX_WIN32)
/* handle signals from master in case of network inactivity */
// 如果timer是無(wú)限或者大于500,則設(shè)置未500
// 即如果選擇定時(shí)方案,會(huì)設(shè)置定時(shí)時(shí)間為500ms
if (timer == NGX_TIMER_INFINITE || timer > 500) {
timer = 500;
}
#endif
}
// 處理驚群現(xiàn)象(nginx是一個(gè)master,多個(gè)work競(jìng)爭(zhēng)請(qǐng)求)
// 驚群現(xiàn)象是指多個(gè)線程/進(jìn)程同時(shí)監(jiān)聽(tīng)一個(gè)socket事件(nginx是多進(jìn)程程序,共享80端口),
// 當(dāng)事件發(fā)生時(shí),會(huì)喚醒所有等待的線程/進(jìn)程,爭(zhēng)搶事件
// 但是最后只有一個(gè)線程/進(jìn)程可以讀取事件成功
// 其他進(jìn)程/線程爭(zhēng)搶失敗后重新等待或者其他操作,這種浪費(fèi)資源的現(xiàn)象叫驚群
// 設(shè)置通過(guò)Accept互斥鎖來(lái)解決驚群現(xiàn)象
// 當(dāng)nginx配置文件中worker的數(shù)量大于1時(shí)
// 且配置文件打開(kāi)了accept_mutex時(shí),ngx_use_accept_mutex會(huì)設(shè)為1
/*
* nginx配置
events {
accept_mutex on; #設(shè)置網(wǎng)路連接序列化,防止驚群現(xiàn)象發(fā)生,默認(rèn)為on
multi_accept on; #設(shè)置一個(gè)進(jìn)程是否同時(shí)接受多個(gè)網(wǎng)絡(luò)連接,默認(rèn)為off
#use epoll; #事件驅(qū)動(dòng)模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
worker_connections 1024; #最大連接數(shù),默認(rèn)為512
}
*/
if (ngx_use_accept_mutex) {
// ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;
// 其中connection_n表示當(dāng)前工作進(jìn)程最大可承受連接數(shù),free_connection_n表現(xiàn)當(dāng)前空閑連接數(shù)
// 假設(shè)當(dāng)前連接數(shù)為x,那么ngx_cycle->connection_n / 8 - connection_n + x => x - 7/8 * connection_n
// 即當(dāng)連接數(shù)超過(guò)最大數(shù)的7/8時(shí),ngx_accept_disabled的值將大于0時(shí),當(dāng)前work處理的連接已經(jīng)達(dá)到飽和
// 此時(shí)work不會(huì)競(jìng)爭(zhēng)新的連接,nginx會(huì)任務(wù)當(dāng)前函數(shù)已經(jīng)經(jīng)歷了一輪事件處理
// 即相應(yīng)的負(fù)載減少了一點(diǎn),并對(duì)ngx_accept_disabled自減
if (ngx_accept_disabled > 0) {
ngx_accept_disabled--;
} else {
// 如果活動(dòng)連接數(shù)還沒(méi)有達(dá)到飽和,則獲取accept鎖
// 多個(gè)work,只有一個(gè)可以得到鎖,
// 且獲取鎖的過(guò)程不是阻塞,獲取成功后,ngx_accept_mutex_held會(huì)設(shè)為1
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
}
// 如果ngx_accept_mutex_held = 1表示成功獲取到鎖
if (ngx_accept_mutex_held) {
// 搶到了accept鎖的進(jìn)程會(huì)被加上了NGX_POST_EVENTS標(biāo)志
// 并加入到post_events隊(duì)列里
// 設(shè)置標(biāo)志,延遲處理事件
flags |= NGX_POST_EVENTS;
} else {
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
}
delta = ngx_current_msec;
// 獲得互斥鎖的進(jìn)程開(kāi)始處理請(qǐng)求
// 主要調(diào)用epoll_wait等待事件
// 并根據(jù)事件類型加入到隊(duì)列ngx_posted_accept_events或者ngx_posted_events
(void) ngx_process_events(cycle, timer, flags);
// delta就是調(diào)用ngx_process_events消耗的時(shí)間
// ngx_current_msec的值是通過(guò)ngx_time_update函數(shù)得到的
// 如果不執(zhí)行ngx_time_update,則delta依然為0
delta = ngx_current_msec - delta;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"timer delta: %M", delta);
// 處理新建連接事件
ngx_event_process_posted(cycle, &ngx_posted_accept_events);
// accept事件一旦處理完成,當(dāng)前進(jìn)程就會(huì)釋放互斥鎖
if (ngx_accept_mutex_held) {
// 釋放鎖
ngx_shmtx_unlock(&ngx_accept_mutex);
}
if (delta) {
// 不斷的從紅黑樹(shù)的節(jié)點(diǎn)中取出時(shí)間值最小的,判斷是否超時(shí)
// 如果超時(shí)就執(zhí)行他們的事件函數(shù),直到最小的時(shí)間不超時(shí)
// 處理超時(shí)事件
ngx_event_expire_timers();
}
// 釋放鎖后開(kāi)始處理ngx_posted_events隊(duì)列中的普通讀寫事件
ngx_event_process_posted(cycle, &ngx_posted_events);
}上一段代碼較長(zhǎng),這里解讀下多做了什么操作:
1. 首先是根據(jù)ngx_timer_resolution的值判斷當(dāng)前定時(shí)策略,這個(gè)值是在nginx.conf文件可選配置:
timer_resolution 10ms
如果timer_resolution 非0,則選擇定時(shí)檢查方案,即設(shè)置定時(shí)時(shí)間,500ms,隔這個(gè)時(shí)間去紅黑樹(shù)中檢查時(shí)候有事件超時(shí),如果有則處理超時(shí)事件。如果ngx_timer_resolution為0,則采用超時(shí)檢測(cè)方案,首先先計(jì)算最快超時(shí)的時(shí)間 timer = ngx_event_find_timer(),然后到了timer這個(gè)時(shí)間去處理超時(shí)事件,接著在計(jì)算一次timer,依次循環(huán)處理超時(shí)事件。
2. 根據(jù)ngx_use_accept_mutex判斷是否打開(kāi)了互斥鎖配置,如果打開(kāi)了互斥鎖配置則開(kāi)始爭(zhēng)搶鎖,否則的話直接處理事件。首先先了解下?tīng)?zhēng)搶鎖的過(guò)程,其核心就是函數(shù)ngx_trylock_accept_mutex:
// 各個(gè)工作線程會(huì)嘗試去獲取鎖
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
// 非阻塞的方式獲取鎖,返回1表示獲取成功,0表示獲取失敗
if (ngx_shmtx_trylock(&ngx_accept_mutex)) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"accept mutex locked");
if (ngx_accept_mutex_held && ngx_accept_events == 0) {
return NGX_OK;
}
// 將cycle->listening中的端口信息添加到epoll事件中
// 即把監(jiān)聽(tīng)socket加入到epoll中進(jìn)行監(jiān)聽(tīng)
if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
ngx_shmtx_unlock(&ngx_accept_mutex);
return NGX_ERROR;
}
// 監(jiān)聽(tīng)完成后會(huì)獲取到
ngx_accept_events = 0;
ngx_accept_mutex_held = 1;
return NGX_OK;
}
// 獲取鎖失敗的情況
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"accept mutex lock failed: %ui", ngx_accept_mutex_held);
// 獲取鎖失敗:本來(lái)就擁有鎖的情況
if (ngx_accept_mutex_held) {
// 本來(lái)?yè)碛墟i就直接將監(jiān)聽(tīng)套接口從自身的事件監(jiān)聽(tīng)機(jī)制刪除
if (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) {
return NGX_ERROR;
}
// 擁有鎖標(biāo)志置位0
ngx_accept_mutex_held = 0;
}
return NGX_OK;
}代碼較累贅,還是畫個(gè)圖說(shuō)明下流程:

拿到鎖后就開(kāi)始處理事件了。開(kāi)始處理之前先 flags |= NGX_POST_EVENTS延遲事件實(shí)際處理的事件,那么是如何延遲的呢?實(shí)際是在函數(shù)ngx_epoll_process_events中:
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
if (flags & NGX_POST_EVENTS) {
queue = rev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(rev, queue);
} else {
rev->handler(rev);
}
}我們已經(jīng)直到影響flags的值是變量ngx_timer_resolution,其中有如下這一段代碼:
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
if (ngx_timer_resolution) {
flags = 0;
} else {
flags = NGX_UPDATE_TIME;
}
}flags被設(shè)置為0或者1,所以執(zhí)行 flags |= NGX_POST_EVENTS會(huì)導(dǎo)致flags & NGX_POST_EVENTS為非零,所以來(lái)自epoll_wait的事件會(huì)被緩存在隊(duì)列里面。
3. 接下來(lái)的操作就是要處理事件了,按照2中爭(zhēng)搶鎖的過(guò)程,搶到鎖的進(jìn)程會(huì)用于事件的監(jiān)聽(tīng)套接口,調(diào)用ngx_process_events方法處理事件,實(shí)際上是調(diào)用了ngx_epoll_module.c中的ngx_epoll_process_events方法:
ngx_epoll_module.c
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
events = epoll_wait(ep, event_list, (int) nevents, timer);
// 定時(shí)方案:回調(diào)函數(shù)會(huì)調(diào)用ngx_timer_signal_handler方法,并設(shè)置ngx_event_timer_alarm = 1,如果沒(méi)有執(zhí)行回
// 調(diào)函數(shù),則不會(huì)執(zhí)行ngx_time_update。
// 超時(shí)方案:flags = 1,則flags & NGX_UPDATE_TIME一定為真,所以每次會(huì)調(diào)用該方法
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
// 更新ngx_current_msec的值
ngx_time_update();
}
// 緩存事件到隊(duì)列
if (flags & NGX_POST_EVENTS) {
queue = rev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(rev, queue);
} else {
rev->handler(rev);
}
}代碼中可以看出,其中accept事件(即監(jiān)聽(tīng)端口上的可讀事件)會(huì)被緩存到隊(duì)列ngx_posted_accept_events,普通事件會(huì)被緩存到隊(duì)列ngx_posted_events。
4. 緩存完事件,接下來(lái)就是處理新建連接事件(accept事件),因?yàn)楫?dāng)前進(jìn)程已經(jīng)監(jiān)聽(tīng)了某個(gè)客戶端的端口,該端口的請(qǐng)求中的可讀事件先要處理下,該讀的數(shù)據(jù)讀完,即處理隊(duì)列ngx_posted_accept_events中的新建連接事件,如果在處理新建連接期間還有新的請(qǐng)求連接事件,會(huì)阻塞,等待下次進(jìn)程獲取鎖后讀取。讀完可讀事件后就執(zhí)行解鎖操作ngx_shmtx_unlock。
5. 鎖釋放完之后就處理連接套接口之后的連接事件了,即保存在隊(duì)列ngx_posted_events中的事件。
void
ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted)
{
ngx_queue_t *q;
ngx_event_t *ev;
while (!ngx_queue_empty(posted)) {
q = ngx_queue_head(posted);
ev = ngx_queue_data(q, ngx_event_t, queue);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"posted event %p", ev);
ngx_delete_posted_event(ev);
ev->handler(ev);
}
}可以看出,就是不斷遍歷隊(duì)列,調(diào)用對(duì)應(yīng)的handler處理事件。
二、介紹事件處理模型的初始化
因?yàn)槲覀兩厦娴闹v解都是以epoll為例的,接下來(lái)就解釋下ngnix如何來(lái)選擇事件處理機(jī)制,首先先看下nginx.conf中的events模塊配置:
events
{
// 選擇使用的事件處理機(jī)制,這里使用epoll
use epoll;
// 是否同時(shí)接受多個(gè)網(wǎng)絡(luò)請(qǐng)求
multi_accept on;
// 是否激活互斥鎖
accept_mutex on;
// 設(shè)置最大可用連接數(shù)
worker_connections 65535;
// 配置http連接的超時(shí)時(shí)間
keepalive_timeout 60;
// 客戶端request請(qǐng)求中header的緩存大小
client_header_buffer_size 4k;
// 靜態(tài)文件的緩存大小和緩存時(shí)間,比如html/css/image
open_file_cache max=65535 inactive=60s;
// 設(shè)置每次檢查緩存有效性的時(shí)間間隔
open_file_cache_valid 80s;
// 靜態(tài)文件有效緩存時(shí)間內(nèi)最少使用次數(shù)
open_file_cache_min_uses 1;
// 設(shè)置是否允許緩存錯(cuò)誤信息
open_file_cache_errors on;
}配置比較詳細(xì),其實(shí)相關(guān)的也就是 use epoll配置。
首先nginx定義了統(tǒng)一的事件處理接口,封裝了各種事件處理機(jī)制(epoll/poll/select等)的執(zhí)行函數(shù):
ngx_event.h
typedef struct {
// 將一個(gè)事件(讀事件/寫事件)添加到事件驅(qū)動(dòng)機(jī)制
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
// 將一個(gè)事件(讀事件/寫事件)從事件驅(qū)動(dòng)中上刪除
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
// 啟用一個(gè)已經(jīng)添加的事件(代碼暫時(shí)未使用)
ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
// 禁用一個(gè)已經(jīng)添加的事件(代碼暫時(shí)未使用)
ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
// 將指定連接關(guān)聯(lián)的描述符加入到多路復(fù)用監(jiān)控里
ngx_int_t (*add_conn)(ngx_connection_t *c);
// 從多路復(fù)用監(jiān)控里刪除指定連接關(guān)聯(lián)的描述符
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
// 僅在多線程環(huán)境下調(diào)用,目前未使用
ngx_int_t (*notify)(ngx_event_handler_pt handler);
// 阻塞等待發(fā)生,并對(duì)發(fā)生的事件逐個(gè)處理
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,ngx_uint_t flags);
// 初始化事件驅(qū)動(dòng)模塊
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
// 回收資源
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;對(duì)封裝的接口定義全局的變量,以及該接口函數(shù)也定義相應(yīng)的全局函數(shù)
ngx_event_actions_t ngx_event_actions ngx_process_events ngx_event_actions.process_events ngx_done_events ngx_event_actions.done ngx_add_event ngx_event_actions.add ngx_del_event ngx_event_actions.del ngx_add_conn ngx_event_actions.add_conn ngx_del_conn ngx_event_actions.del_conn ngx_notify ngx_event_actions.notify
所以添加一個(gè)事件(讀/寫)只需要調(diào)用ngx_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)。那么重點(diǎn)就是如何對(duì)ngx_event_actions進(jìn)行賦值?搜索event/modules會(huì)發(fā)現(xiàn)在各種事件處理機(jī)制的模塊里面都存在對(duì)ngx_event_actions的賦值:
D:\mycode\nginx\src\event\modules\ngx_devpoll_module.c: 186: ngx_event_actions = ngx_devpoll_module_ctx.actions; D:\mycode\nginx\src\event\modules\ngx_epoll_module.c: 189: ngx_event_actions = ngx_epoll_module_ctx.actions D:\mycode\nginx\src\event\modules\ngx_eventport_module.c: 279: ngx_event_actions = ngx_eventport_module_ctx.actions; D:\mycode\nginx\src\event\modules\ngx_kqueue_module.c: 224: ngx_event_actions = ngx_kqueue_module_ctx.actions; D:\mycode\nginx\src\event\modules\ngx_poll_module.c: 96: ngx_event_actions = ngx_poll_module_ctx.actions; D:\mycode\nginx\src\event\modules\ngx_select_module.c: 105: ngx_event_actions = ngx_select_module_ctx.actions; D:\mycode\nginx\src\event\modules\ngx_win32_select_module.c: 106: ngx_event_actions = ngx_select_module_ctx.actions;
而系統(tǒng)采用哪個(gè)賦值語(yǔ)句取決于用戶在events模塊的配置中的use epoll
具體初始化的過(guò)程:
static char *
ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_conf_init_uint_value(ecf->use, module->ctx_index);
}
#define ngx_conf_init_size_value(conf, default)
// 用戶沒(méi)有指定則設(shè)置默認(rèn)的值
if (conf == NGX_CONF_UNSET_SIZE) {
conf = default;
}以上配置項(xiàng)通過(guò)函數(shù)ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module)獲取到,即ecf->use的值是epoll值的序號(hào),而序號(hào)的設(shè)置在ngx_module.c中:
ngx_int_t
ngx_preinit_modules(void)
{
ngx_uint_t i;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = i;
ngx_modules[i]->name = ngx_module_names[i];
}
ngx_modules_n = i;
ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;
return NGX_OK;
}在ngx_preinit_modules會(huì)循環(huán)遍歷所有ngx_modules中的值,并對(duì)模塊的index值賦值,用于標(biāo)識(shí)這個(gè)模塊,所以這里存在疑問(wèn)的是ngx_modules的值來(lái)自于哪里?查看源碼,發(fā)現(xiàn)在nginx.c中存在賦值語(yǔ)句 modules = ngx_dlsym(handle, "ngx_modules"):
ngx_event.c:
ngx_event_process_init(ngx_cycle_t *cycle)
{
// 返回events模塊的配置集合
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
// 遍歷所有模塊
for (m = 0; cycle->modules[m]; m++) {
if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
// 如果不等于ecf->use的值就繼續(xù)遍歷
if (cycle->modules[m]->ctx_index != ecf->use) {
continue;
}
//找到用戶指定的事件處理機(jī)制
module = cycle->modules[m]->ctx;
// 執(zhí)行該模塊的初始化函數(shù)
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
/* fatal */
exit(2);
}
break;
}
}所以我們可以知道nginx對(duì)事件具體的處理邏輯定義在event/modules各個(gè)處理機(jī)制的模塊里,我們?nèi)∑渲幸粋€(gè)為例(ngx_epoll_module.c),在上面的賦值語(yǔ)句ngx_event_actions = ngx_epoll_module_ctx.actions,其中ngx_event_actions封裝了nginx統(tǒng)一的事件處理調(diào)用函數(shù),而ngx_epoll_module_ctx則定義了epoll模塊的上下文信息,是ngx_event_module_t類型的靜態(tài)變量,ngx_epoll_module.c中有如下定義:
typedef struct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;具體的賦值語(yǔ)句就是定義靜態(tài)變量ngx_epoll_module_ctx:
static ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */
// 對(duì)actions的賦值
{
// 添加事件(讀/寫事件)
ngx_epoll_add_event, /* add an event */
ngx_epoll_del_event, /* delete an event */
ngx_epoll_add_event, /* enable an event */
ngx_epoll_del_event, /* disable an event */
ngx_epoll_add_connection, /* add an connection */
ngx_epoll_del_connection, /* delete an connection */
#if (NGX_HAVE_EVENTFD)
ngx_epoll_notify, /* trigger a notify */
#else
NULL, /* trigger a notify */
#endif
ngx_epoll_process_events, /* process the events */
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};到這里,就已經(jīng)通過(guò)ngx_event_actions = ngx_epoll_module_ctx.actions賦值語(yǔ)句將epoll的處理函數(shù)賦值給了nginx的全局變量ngx_event_actions,所以當(dāng)我們調(diào)用全局函數(shù)ngx_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)實(shí)際上就是調(diào)用ngx_epoll_add_event。
到此,相信大家對(duì)“Nginx事件處理模塊怎么理解”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
分享文章:Nginx事件處理模塊怎么理解
本文URL:http://www.chinadenli.net/article36/geicsg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營(yíng)銷型網(wǎng)站建設(shè)、建站公司、品牌網(wǎng)站建設(shè)、網(wǎng)站收錄、微信小程序、網(wǎng)站策劃
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
移動(dòng)網(wǎng)站建設(shè)知識(shí)