摘要:我們這里以單進程啟動為例中的函數(shù)調(diào)用這個函數(shù)回循環(huán)調(diào)用中的事件循環(huán)的核心函數(shù)是。這個方法僅在方法中調(diào)用,它是處理,分發(fā)事件的核心初始化事件驅(qū)動模塊的方法退出事件驅(qū)動模塊前調(diào)用的方法。讀事件的回調(diào)函數(shù)是這樣就進入了框架處理流程
我們這里以單進程啟動為例
nginx.c中的main 函數(shù)調(diào)用ngx_single_process_cycle
這個函數(shù)回循環(huán)調(diào)用
ngx_process_cycle.c 中的
for ( ;; ) { .... ngx_process_events_and_timers .... }
事件循環(huán)的核心函數(shù)是 ngx_process_events_and_timers 。這個函數(shù)主要干了四件 事情:搶占 accept mutex,等待并分發(fā)事件,處理 accept 事件,處理其他io事件
我們這里只介紹等待分發(fā)事件
ngx_event.c 中的
(void) ngx_process_events(cycle, timer, flags);
這里開始 wait并分發(fā)事件, 我們來可以來看一下這個函數(shù)
可以看到在 ngx_event.h 中的一個宏
#define ngx_process_events ngx_event_actions.process_events
我們來看一下 ngx_event_actions 這個數(shù)據(jù)結(jié)構(gòu)
typedef struct { /* 添加事件方法,它將負責(zé)把1個感興趣的事件添加到操作系統(tǒng)提供的事件驅(qū)動機制(如epoll,kqueue等)中, 這樣,在事件發(fā)生之后,將可以在調(diào)用下面的process_envets時獲取這個事件。 */ ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); /* 刪除事件方法,它將一個已經(jīng)存在于事件驅(qū)動機制中的事件一出,這樣以后即使這個事件發(fā)生,調(diào)用process_events方法時也無法再獲取這個事件 */ ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); /* 啟用一個事件,目前事件框架不會調(diào)用這個方法,大部分事件驅(qū)動模塊對于該方法的實現(xiàn)都是與上面的add方法完全一致的 */ ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); /* 禁用一個事件,目前事件框架不會調(diào)用這個方法,大部分事件驅(qū)動模塊對于該方法的實現(xiàn)都是與上面的del方法一致 */ ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); /* 向事件驅(qū)動機制中添加一個新的連接,這意味著連接上的讀寫事件都添加到事件驅(qū)動機制中了 */ ngx_int_t (*add_conn)(ngx_connection_t *c); // 從事件驅(qū)動機制中一出一個連續(xù)的讀寫事件 ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags); // 僅在多線程環(huán)境下會被調(diào)用,目前,nginx在產(chǎn)品環(huán)境下還不會以多線程方式運行。 ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait); // 在正常的工作循環(huán)中,將通過調(diào)用process_events方法來處理事件。 // 這個方法僅在ngx_process_events_and_timers方法中調(diào)用,它是處理,分發(fā)事件的核心 ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); // 初始化事件驅(qū)動模塊的方法 ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer); // 退出事件驅(qū)動模塊前調(diào)用的方法。 void (*done)(ngx_cycle_t *cycle); } ngx_event_actions_t; extern ngx_event_actions_t ngx_event_actions;
這個數(shù)據(jù)結(jié)構(gòu)中定義了很多函數(shù)指針,
這里我們的配置是
events { use epoll; worker_connections 1024; #所以nginx支持的總連接數(shù)就等于worker_processes * worker_connections }
使用的是 epoll 事件模塊,在epoll 模塊初始化的時候調(diào)用
ngx_epoll_module.c 中的ngx_epoll_init的函數(shù)
其中給ngx_event_actions賦值
ngx_event_actions = ngx_epoll_module_ctx.actions
我們來看下 ngx_epoll_module_ctx
結(jié)構(gòu)類型是
typedef struct { // 事件模塊的名稱 ngx_str_t *name; // 在解析配置項前,這個回調(diào)方法用于創(chuàng)建存儲配置項參數(shù)的結(jié)構(gòu)體 void *(*create_conf)(ngx_cycle_t *cycle); // 在解析配置項完成后,init_conf方法會被調(diào)用,用于綜合處理當(dāng)前事件模塊感興趣的全部配置項。 char *(*init_conf)(ngx_cycle_t *cycle, void *conf); // 對于事件驅(qū)動機制,每個事件模塊需要實現(xiàn)的10個抽象方法 ngx_event_actions_t actions; } ngx_event_module_t;
初始化
//epoll是個event模塊 ngx_event_module_t ngx_epoll_module_ctx = { &epoll_name, ngx_epoll_create_conf, /* create configuration */ ngx_epoll_init_conf, /* init configuration */ { 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 */ NULL, /* process the changes */ ngx_epoll_process_events, /* process the events */ ngx_epoll_init, /* init the events */ ngx_epoll_done, /* done the events */ } };
這些事件處理函數(shù)都在 ngx_epoll_module.c 這個文件中,大家可以看一下源碼
綜上,根據(jù)我們的配置, ngx_event.c 中的 ngx_process_events
實際調(diào)用的是 ngx_epoll_module.c 中的 ngx_epoll_process_events
這個函數(shù)有點長,我們找些關(guān)鍵的點看一下,
//一開始就是等待事件,最長等待時間為timer;nginx為事件專門用紅黑樹維護了一個計時器 events = epoll_wait(ep, event_list, (int) nevents, timer);
所有收集到的事件都放在了event_list 中,我們來看一下這個event_list
static struct epoll_event *event_list; struct epoll_event { uint32_t events; epoll_data_t data; }; typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t;
下面就開始對這些事件就行處理, 先加鎖,
ngx_mutex_lock(ngx_posted_events_mutex); //循環(huán)開始處理收到的所有事件 for (i = 0; i < events; i++) { c = event_list[i].data.ptr; instance = (uintptr_t) c & 1; c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1); rev = c->read; if (c->fd == -1 || rev->instance != instance) { /* * the stale event from a file descriptor * that was just closed in this iteration */ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll: stale event %p", c); continue; } //取得發(fā)生一個事件 revents = event_list[i].events; ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll: fd:%d ev:%04XD d:%p", c->fd, revents, event_list[i].data.ptr); //記錄wait的錯誤返回狀態(tài) if (revents & (EPOLLERR|EPOLLHUP)) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll_wait() error on fd:%d ev:%04XD", c->fd, revents); } #if 0 if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "strange epoll_wait() events fd:%d ev:%04XD", c->fd, revents); } #endif //該事件是一個讀事件,并該連接上注冊的讀事件是active的 if ((revents & (EPOLLERR|EPOLLHUP)) && (revents & (EPOLLIN|EPOLLOUT)) == 0) { /* * if the error events were returned without EPOLLIN or EPOLLOUT, * then add these flags to handle the events at least in one * active handler */ revents |= EPOLLIN|EPOLLOUT; } if ((revents & EPOLLIN) && rev->active) { if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) { rev->posted_ready = 1; } else { rev->ready = 1; } //事件放入到相應(yīng)的隊列中 if (flags & NGX_POST_EVENTS) { queue = (ngx_event_t **) (rev->accept ? &ngx_posted_accept_events : &ngx_posted_events); ngx_locked_post_event(rev, queue); } else { rev->handler(rev); } } wev = c->write; if ((revents & EPOLLOUT) && wev->active) { if (c->fd == -1 || wev->instance != instance) { /* * the stale event from a file descriptor * that was just closed in this iteration */ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll: stale event %p", c); continue; } if (flags & NGX_POST_THREAD_EVENTS) { wev->posted_ready = 1; } else { wev->ready = 1; } if (flags & NGX_POST_EVENTS) { ngx_locked_post_event(wev, &ngx_posted_events); } else { wev->handler(wev); } } } ngx_mutex_unlock(ngx_posted_events_mutex);
先加鎖,對event_list 中的事件循環(huán)處理,
在每個循環(huán)中,
先獲取這個事件所在的連接, 然后判斷是讀事件還是寫事件, 然后調(diào)用注冊在這個事件上的的回調(diào)函數(shù)。 讀事件的回調(diào)函數(shù)是
ngx_http_init_connection 這樣就進入了 HTTP框架處理流程
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/39136.html
摘要:反向代理反向代理反向代理負載均衡鑒權(quán)限流等邏輯架構(gòu)在邏輯上分為入口層,模塊化的功能處理層,系統(tǒng)調(diào)用層。多個共同監(jiān)聽事件并處理,反向代理會把請求轉(zhuǎn)發(fā)給后端服務(wù)。 一.概述 本文將深入剖析nginx的架構(gòu)。 第一部分介紹nginx現(xiàn)有框架,用典型的4+1視圖闡述,包括邏輯架構(gòu),開發(fā)架構(gòu),運行架構(gòu),物理架構(gòu),功能用例,nginx為單機服務(wù),不考慮物理架構(gòu)。其中功能用例概述nginx功能;邏輯...
摘要:反向代理反向代理反向代理負載均衡鑒權(quán)限流等邏輯架構(gòu)在邏輯上分為入口層,模塊化的功能處理層,系統(tǒng)調(diào)用層。多個共同監(jiān)聽事件并處理,反向代理會把請求轉(zhuǎn)發(fā)給后端服務(wù)。 一.概述 本文將深入剖析nginx的架構(gòu)。 第一部分介紹nginx現(xiàn)有框架,用典型的4+1視圖闡述,包括邏輯架構(gòu),開發(fā)架構(gòu),運行架構(gòu),物理架構(gòu),功能用例,nginx為單機服務(wù),不考慮物理架構(gòu)。其中功能用例概述nginx功能;邏輯...
摘要:和服務(wù)關(guān)系最密切的進程是中的進程組,絕大部分業(yè)務(wù)處理都在該進程中進行。隨后觸發(fā)一個事件各組件通過該事件進行配置文件加載路由注冊。事件每個請求到來時僅僅會觸發(fā)事件。服務(wù)器生命周期和服務(wù)基本一致,詳情參考源碼剖析功能實現(xiàn) 作者:bromine鏈接:https://www.jianshu.com/p/4c0...來源:簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新的排版。S...
摘要:深入剖析事件有兩種模型水平觸發(fā)接收緩沖區(qū)不為空有數(shù)據(jù)可讀讀事件一直觸發(fā)發(fā)送緩沖區(qū)不滿可以繼續(xù)寫入數(shù)據(jù)寫事件一直觸發(fā)符合思維習(xí)慣,返回的事件就是的狀態(tài)邊沿觸發(fā)的接收緩沖區(qū)狀態(tài)變化時觸發(fā)讀事件,即空的接收緩沖區(qū)剛接收到數(shù)據(jù)時觸發(fā)讀事件的發(fā)送緩沖 epoll LT/ET 深入剖析 EPOLL事件有兩種模型: Level Triggered (LT) 水平觸發(fā).socket接收緩沖區(qū)不為空 有...
閱讀 2570·2021-09-30 10:00
閱讀 3505·2021-09-22 10:54
閱讀 6274·2021-09-07 10:28
閱讀 2957·2019-08-29 13:53
閱讀 753·2019-08-29 12:42
閱讀 968·2019-08-26 13:51
閱讀 1266·2019-08-26 13:32
閱讀 3029·2019-08-26 10:39