nginx怎么處理http請(qǐng)求

一、Event事件http框架的交互

? ? ? ? 在接收完http請(qǐng)求行、http請(qǐng)求頭部后,會(huì)調(diào)用ngx_http_process_request這個(gè)函數(shù)開(kāi)始處理http請(qǐng)求。因?yàn)橐粋€(gè)http請(qǐng)求由11個(gè)處理階段組成,而每一個(gè)處理階段都允許多個(gè)http模塊介入,因此在這個(gè)函數(shù)中,將調(diào)度各個(gè)階段的http模塊共同完成這個(gè)請(qǐng)求。

//接收到http請(qǐng)求行與請(qǐng)求頭后,http的處理流程,是第一個(gè)http處理請(qǐng)求的讀事件回調(diào)? //這個(gè)函數(shù)執(zhí)行后,將把讀寫(xiě)事件的回調(diào)設(shè)置為ngx_http_request_handler。這樣下次再有事件時(shí)? //將調(diào)用ngx_http_request_handler函數(shù)來(lái)處理,而不會(huì)再調(diào)用ngx_http_process_request了? static?void?ngx_http_process_request(ngx_http_request_t?*r)? {? ??ngx_connection_t?*c;? ??c?=?r->connection;? ??//因?yàn)橐呀?jīng)接收完http請(qǐng)求行、請(qǐng)求頭部了,準(zhǔn)備調(diào)用各個(gè)http模塊處理請(qǐng)求了。? ??//因此需要接收任何來(lái)自客戶端的讀事件,也就不存在接收http請(qǐng)求頭部超時(shí)問(wèn)題? ??if?(c->read->timer_set)?? ??{? ????ngx_del_timer(c->read);? ??}? ??//重新設(shè)置當(dāng)前連接的讀寫(xiě)事件回調(diào)? ??c->read->handler?=?ngx_http_request_handler;? ??c->write->handler?=?ngx_http_request_handler;? ??//設(shè)置http請(qǐng)求對(duì)象的讀事件回調(diào),這個(gè)回調(diào)不做任何的事情。? ??//那http請(qǐng)求對(duì)象的讀事件回調(diào),與上面的連接對(duì)應(yīng)的讀事件回調(diào)有什么關(guān)系呢?? ??//當(dāng)讀事件發(fā)生后,連接對(duì)應(yīng)的讀事件回調(diào)ngx_http_request_handler會(huì)被調(diào)用,? ??//在這個(gè)回調(diào)內(nèi)會(huì)調(diào)用http請(qǐng)求對(duì)象的讀事件回調(diào)ngx_http_block_reading,而這個(gè)回調(diào)是? ??//不會(huì)做任何事件的,因此相當(dāng)于忽略了讀事件。因?yàn)橐呀?jīng)接收完了請(qǐng)求行請(qǐng)求頭,現(xiàn)在要做的是調(diào)用各個(gè)http模塊,? ??//對(duì)接收到的請(qǐng)求行請(qǐng)求頭進(jìn)行處理? ??r->read_event_handler?=?ngx_http_block_reading;? ? ??//調(diào)用各個(gè)http模塊協(xié)同處理這個(gè)請(qǐng)求? ??ngx_http_handler(r);? ??//處理子請(qǐng)求? ??ngx_http_run_posted_requests(c);? }

????? ngx_http_process_request函數(shù)只會(huì)被調(diào)用一次。如果一次調(diào)度并不能處理完11個(gè)http階段,那會(huì)將連接對(duì)象對(duì)應(yīng)的讀事件、寫(xiě)事件回調(diào)設(shè)置為ngx_http_request_handler。而請(qǐng)求對(duì)象的讀事件設(shè)置為ngx_http_block_reading, 請(qǐng)求對(duì)象的寫(xiě)事件回調(diào)設(shè)置為ngx_http_core_run_phases, 這個(gè)回調(diào)在ngx_http_handler內(nèi)設(shè)置。這樣在事件再次到來(lái)時(shí)不會(huì)調(diào)用

ngx_http_process_request函數(shù)處理了。那event事件模塊的讀寫(xiě)事件回調(diào)與http請(qǐng)求對(duì)象的讀寫(xiě)事件回調(diào)有什么關(guān)系呢?

nginx怎么處理http請(qǐng)求

//http請(qǐng)求處理讀與寫(xiě)事件的回調(diào),在ngx_http_process_request函數(shù)中設(shè)置。? //這個(gè)函數(shù)中將會(huì)調(diào)用http請(qǐng)求對(duì)象的讀寫(xiě)事件回調(diào)。將event事件模塊與http框架關(guān)聯(lián)起來(lái)? static?void?ngx_http_request_handler(ngx_event_t?*ev)? {? ??//如果同時(shí)發(fā)生讀寫(xiě)事件,則只有寫(xiě)事件才會(huì)觸發(fā)。寫(xiě)事件優(yōu)先級(jí)更高? ??if?(ev->write)?? ??{? ????r->write_event_handler(r);??//在函數(shù)ngx_http_handler設(shè)置為:ngx_http_core_run_phases? ? ??}? ??else? ??{? ????r->read_event_handler(r);??//在函數(shù)ngx_http_process_request設(shè)置為:ngx_http_block_reading? ??}? ? ??//處理子請(qǐng)求? ??ngx_http_run_posted_requests(c);? }

可以看到,連接對(duì)象的讀事件回調(diào)中,會(huì)調(diào)用http請(qǐng)求對(duì)象的讀事件回調(diào)。連接對(duì)象的寫(xiě)事件回調(diào)會(huì)調(diào)用http請(qǐng)求對(duì)象的寫(xiě)事件回調(diào)。

nginx怎么處理http請(qǐng)求

? ? ? ? 圖中可看出,在event的讀事件發(fā)生時(shí),epoll返回后會(huì)調(diào)用讀事件的回調(diào)ngx_http_request_handler。在這個(gè)讀事件回調(diào)中,又會(huì)調(diào)用http框架,也就是http請(qǐng)求對(duì)象的讀事件回調(diào)ngx_http_block_reading,這個(gè)http請(qǐng)求對(duì)象的讀事件回調(diào)是不做任何事情的,相當(dāng)于忽略讀事件。因此http框架將會(huì)返回到事件模塊。那為什么要忽略讀事件呢?因?yàn)閔ttp請(qǐng)求行、請(qǐng)求頭部都已經(jīng)全部接收完成了, 現(xiàn)在要做的是調(diào)度各個(gè)http模塊共同協(xié)作,完成對(duì)接收到的請(qǐng)求行,請(qǐng)求頭部的處理。因此不需要接收來(lái)自客戶端任何數(shù)據(jù)了。

? ? ? ??nginx怎么處理http請(qǐng)求

? ? ? ? 對(duì)于寫(xiě)事件的處理就復(fù)雜多了, ?在event的寫(xiě)事件發(fā)生時(shí),epoll返回后會(huì)調(diào)用寫(xiě)事件的回調(diào)ngx_http_request_handler,在這個(gè)寫(xiě)事件回調(diào)中,又會(huì)調(diào)用http框架,也就是http請(qǐng)求對(duì)象的寫(xiě)事件回調(diào)ngx_http_core_run_phases。這個(gè)http框架的回調(diào)會(huì)調(diào)度介入11個(gè)請(qǐng)求階段的各個(gè)http模塊的hander方法,共同完成http請(qǐng)求。

二、調(diào)度http模塊處理請(qǐng)求

? ? ? ? 在上面代碼中,會(huì)調(diào)度ngx_http_core_run_phases這個(gè)函數(shù),使得各個(gè)http模塊能介入到http請(qǐng)求中來(lái)。而這個(gè)函數(shù)是在ngx_http_handler設(shè)置的。

//調(diào)用各個(gè)http模塊協(xié)同處理這個(gè)請(qǐng)求? void?ngx_http_handler(ngx_http_request_t?*r)? {? ??//不需要進(jìn)行內(nèi)部跳轉(zhuǎn)。什么是內(nèi)部跳轉(zhuǎn)??例如有個(gè)location結(jié)構(gòu),里面的? ??//? ??if?(!r->internal)??? ??{? ????//將數(shù)組序號(hào)設(shè)為0,表示從數(shù)組第一個(gè)元素開(kāi)始處理http請(qǐng)求? ????//這個(gè)下標(biāo)很重要,決定了當(dāng)前要處理的是11個(gè)階段中的哪一個(gè)階段,? ????//以及由這個(gè)階段的哪個(gè)http模塊處理請(qǐng)求? ????r->phase_handler?=?0;? ??}?? ??else?? ??{? ????//需要做內(nèi)部跳轉(zhuǎn)? ????cmcf?=?ngx_http_get_module_main_conf(r,?ngx_http_core_module);? ????//將序號(hào)設(shè)置為server_rewrite_index? ????r->phase_handler?=?cmcf->phase_engine.server_rewrite_index;? ??}? ??//設(shè)置請(qǐng)求對(duì)象的寫(xiě)事件回調(diào),這個(gè)回調(diào)將會(huì)調(diào)度介入11個(gè)http階段的各個(gè)http模塊? ??//共同完成對(duì)請(qǐng)求的處理? ??r->write_event_handler?=?ngx_http_core_run_phases;? ??//開(kāi)始調(diào)度介入11個(gè)http階段的各個(gè)http模塊? ??ngx_http_core_run_phases(r);? }

而ngx_http_core_run_phases函數(shù)就很簡(jiǎn)單了,調(diào)度介入11個(gè)http處理階段的所有http模塊的checker方法。

//調(diào)用各個(gè)http模塊協(xié)同處理這個(gè)請(qǐng)求,?checker函數(shù)內(nèi)部會(huì)修改phase_handler? void?ngx_http_core_run_phases(ngx_http_request_t?*r)? {? ??cmcf?=?ngx_http_get_module_main_conf(r,?ngx_http_core_module);? ??ph?=?cmcf->phase_engine.handlers;? ??//調(diào)用各個(gè)http模塊的checker方法,使得各個(gè)http模塊可以介入http請(qǐng)求? ??while?(ph[r->phase_handler].checker)? ??{? ????rc?=?ph[r->phase_handler].checker(r,?&ph[r->phase_handler]);? ????//從http模塊返回ngx_ok,http框架則會(huì)把控制全交還給事件模塊? ????if?(rc?==?ngx_ok)????? ????{? ??????return;? ????}? ??}

假設(shè)階段2有三個(gè)http模塊介入了http請(qǐng)求, 階段3有一個(gè)模塊介入了http請(qǐng)求、階段4也有一個(gè)模塊介入了請(qǐng)求。當(dāng)開(kāi)始處理階段2時(shí),將調(diào)用階段2中的所有http模塊進(jìn)行處理,此時(shí)phase_handler指向階段2的開(kāi)始位置。之后每處理完階段2中的一個(gè)模塊時(shí),phase_handler指向階段2的下一個(gè)模塊,直到階段2處理完成。

nginx怎么處理http請(qǐng)求

? ? ? ? 當(dāng)階段2中的所有http模塊都處理完成時(shí),phase_handler將指向階段3

nginx怎么處理http請(qǐng)求

? ? ? ? 因階段3只有一個(gè)http模塊,因此當(dāng)階段3中的所有http模塊都處理完成時(shí),phase_handler將指向階段4

nginx怎么處理http請(qǐng)求

??????? 那這個(gè)handlers數(shù)組是什么時(shí)候創(chuàng)建的呢? 每一個(gè)http模塊的checker回調(diào)又是做什么呢? 接下來(lái)將分析這兩個(gè)問(wèn)題

三、11個(gè)http請(qǐng)求階段數(shù)組創(chuàng)建

? ? ? ? 在解析nginx.conf配置文件時(shí),解析到http塊時(shí),會(huì)調(diào)用ngx_http_block這個(gè)函數(shù)開(kāi)始解析http塊。在這個(gè)函數(shù)中,也會(huì)把所有需要介入到11個(gè)http請(qǐng)求階段的http模塊,注冊(cè)到數(shù)組中。

//開(kāi)始解析http塊? static?char?*?ngx_http_block(ngx_conf_t?*cf,?ngx_command_t?*cmd,?void?*conf)? {? ??//http配置解析完成后的后續(xù)處理,使得各個(gè)http模塊可以介入到11個(gè)http階段? ??for?(m?=?0;?ngx_modules[m];?m++)?? ??{? ????if?(ngx_modules[m]->type?!=?ngx_http_module)?? ????{? ??????continue;? ????}? ? ????module?=?ngx_modules[m]->ctx;? ????if?(module->postconfiguration)?? ????{? ??????//每一個(gè)http模塊的在這個(gè)postconfiguration函數(shù)中,都可以把自己注冊(cè)到11個(gè)http階段? ??????if?(module->postconfiguration(cf)?!=?ngx_ok)?? ??????{? ????????return?ngx_conf_error;? ??????}? ????}? ??}? }

??????? 例如ngx_http_static_module靜態(tài)模塊,會(huì)將自己介入11個(gè)http階段的ngx_http_content_phase階段回調(diào)設(shè)置為ngx_http_static_handler

//靜態(tài)模塊將自己注冊(cè)到11個(gè)http請(qǐng)求階段中的ngx_http_content_phase階段? static?ngx_int_t?ngx_http_static_init(ngx_conf_t?*cf)? {? ??cmcf?=?ngx_http_conf_get_module_main_conf(cf,?ngx_http_core_module);? ??h?=?ngx_array_push(&cmcf->phases[ngx_http_content_phase].handlers);? ? ??//靜態(tài)模塊在ngx_http_content_phase階段的處理方法? ??*h?=?ngx_http_static_handler;? ? ??return?ngx_ok;? }

例如:?ngx_http_access_module訪問(wèn)權(quán)限模塊,會(huì)將自己介入11個(gè)http階段的ngx_http_access_phase階段回調(diào)設(shè)置為ngx_http_access_handler

//訪問(wèn)權(quán)限模塊將自己注冊(cè)到11個(gè)http請(qǐng)求階段中的ngx_http_access_phase階段? static?ngx_int_t?ngx_http_access_init(ngx_conf_t?*cf)? {? ??cmcf?=?ngx_http_conf_get_module_main_conf(cf,?ngx_http_core_module);? ??h?=?ngx_array_push(&cmcf->phases[ngx_http_access_phase].handlers);? ??? ??//訪問(wèn)權(quán)限模塊在ngx_http_access_phase階段的處理方法? ??*h?=?ngx_http_access_handler;? ??? ??return?ngx_ok;? }

上面的這些操作,只是把需要介入到11個(gè)http階段的http模塊保存到了ngx_http_core_main_conf_t中的phases成員中,并沒(méi)有保存到phase_engine中。那什么時(shí)候?qū)hases的內(nèi)容保存到phase_engine中呢? ?還是在ngx_http_block函數(shù)中完成

//開(kāi)始解析http塊? static?char?*?ngx_http_block(ngx_conf_t?*cf,?ngx_command_t?*cmd,?void?*conf)? {? ????//初始化請(qǐng)求的各個(gè)階段? ??if?(ngx_http_init_phase_handlers(cf,?cmcf)?!=?ngx_ok)?? ??{? ????return?ngx_conf_error;? ??}? }

假設(shè)階段1有一個(gè)http模塊介入請(qǐng)求,階段2有三個(gè)http模塊介入請(qǐng)求、階段3也有一個(gè)http模塊介入請(qǐng)求。則ngx_http_init_phase_handlers這個(gè)函數(shù)調(diào)用后,從ngx_http_phase_t ? phases[11]數(shù)組轉(zhuǎn)換到ngx_http_phase_handler_t ? ?handlers數(shù)組的過(guò)程如下圖所示:

nginx怎么處理http請(qǐng)求

//初始化請(qǐng)求的各個(gè)階段? static?ngx_int_t?ngx_http_init_phase_handlers(ngx_conf_t?*cf,?ngx_http_core_main_conf_t?*cmcf)? {? ??//11個(gè)http請(qǐng)求階段,每一個(gè)階段都可以有多個(gè)http模塊介入。? ??//這里統(tǒng)計(jì)11個(gè)節(jié)點(diǎn)一共有多個(gè)少http模塊。以便下面開(kāi)辟空間? ??for?(i?=?0;?i?phases[i].handlers.nelts;? ??}? ??//開(kāi)辟空間,存放介入11個(gè)處理階段的所有http模塊的回調(diào)? ??ph?=?ngx_pcalloc(cf->pool,n?*?sizeof(ngx_http_phase_handler_t)?+?sizeof(void?*));? ??cmcf->phase_engine.handlers?=?ph;? ??n?=?0;? ? ??//對(duì)于每一個(gè)http處理階段,給該階段中所有介入的http模塊賦值? ??for?(i?=?0;?i?phases[i].handlers.elts;? ? ????switch?(i)?? ????{? ??????case?ngx_http_server_rewrite_phase://根據(jù)請(qǐng)求的uri查找location之前,修改請(qǐng)求的uri階段? ????????if?(cmcf->phase_engine.server_rewrite_index?==?(ngx_uint_t)?-1)?? ????????{? ??????????cmcf->phase_engine.server_rewrite_index?=?n;?//重定向模塊在數(shù)組中的位置? ????????}? ????????checker?=?ngx_http_core_rewrite_phase;???//每一個(gè)階段的checker回調(diào)? ????????break;? ??????case?ngx_http_find_config_phase://根據(jù)請(qǐng)求的uri查找location階段(只能由http框架實(shí)現(xiàn))? ????????find_config_index?=?n;? ????????ph->checker?=?ngx_http_core_find_config_phase;? ????????n++;? ????????ph++;? ????????continue;? ??????case?ngx_http_rewrite_phase:??//根據(jù)請(qǐng)求的rui查找location之后,修改請(qǐng)求的uri階段? ????????if?(cmcf->phase_engine.location_rewrite_index?==?(ngx_uint_t)?-1)? ????????{? ??????????cmcf->phase_engine.location_rewrite_index?=?n;? ????????}? ????????checker?=?ngx_http_core_rewrite_phase;? ????????break;? ??????case?ngx_http_post_rewrite_phase:?//ngx_http_rewrite_phase階段修改rul后,防止遞歸修改uri導(dǎo)致死循環(huán)階段? ????????if?(use_rewrite)?? ????????{? ??????????ph->checker?=?ngx_http_core_post_rewrite_phase;? ??????????ph->next?=?find_config_index;//目的是為了地址重寫(xiě)后,跳轉(zhuǎn)到ngx_http_find_config_phase階段,根據(jù)? ????????????????????????//url重寫(xiě)查找location? ??????????n++;? ??????????ph++;? ????????}? ????????continue;? ??????case?ngx_http_access_phase:?????//是否允許訪問(wèn)服務(wù)器階段? ????????checker?=?ngx_http_core_access_phase;? ????????n++;? ????????break;? ??????case?ngx_http_post_access_phase:??//根據(jù)ngx_http_access_phase階段的錯(cuò)誤碼,給客戶端構(gòu)造響應(yīng)階段? ????????if?(use_access)?? ????????{? ??????????ph->checker?=?ngx_http_core_post_access_phase;? ??????????ph->next?=?n;? ??????????ph++;? ????????}? ????????continue;? ??????case?ngx_http_try_files_phase:???//try_file階段? ????????if?(cmcf->try_files)?? ????????{? ??????????ph->checker?=?ngx_http_core_try_files_phase;? ??????????n++;? ??????????ph++;? ????????}? ????????continue;? ??????case?ngx_http_content_phase:????//處理http請(qǐng)求內(nèi)容階段,大部分http模塊最愿意介入的階段? ????????checker?=?ngx_http_core_content_phase;? ????????break;? ??????default:? ????????//ngx_http_post_read_phase,?? ????????//ngx_http_preaccess_phase,?? ????????//ngx_http_log_phase三個(gè)階段的checker方法? ????????checker?=?ngx_http_core_generic_phase;? ????}? ????n?+=?cmcf->phases[i].handlers.nelts;? ????//每一個(gè)階段中所介入的所有http模塊,同一個(gè)階段中的所有http模塊有唯一的checker回調(diào),? ????//但handler回調(diào)每一個(gè)模塊自己實(shí)現(xiàn)? ????for?(j?=?cmcf->phases[i].handlers.nelts?-?1;?j?>=0;?j--)?? ????{? ??????ph->checker?=?checker;????? ??????ph->handler?=?h[j];? ??????ph->next?=?n;? ??????ph++;? ????}? ??}? ??return?ngx_ok;? }

四、http階段的checker回調(diào)

? ? ? ? 在11個(gè)http處理階段中,每一個(gè)階段都有一個(gè)checker函數(shù),當(dāng)然有些階段的checker函數(shù)是相同的。對(duì)每一個(gè)處理階段,介入這個(gè)階段的所有http模塊都共用同一個(gè)checker函數(shù)。這些checker函數(shù)的作用是調(diào)度介入這個(gè)階段的所有http模塊的handler方法,或者切換到一下個(gè)http請(qǐng)求階段。下面分析下ngx_http_post_read_phase,ngx_http_preaccess_phase,ngx_http_log_phase三個(gè)階段的checker方法。

//ngx_http_post_read_phase,?? //ngx_http_preaccess_phase,?? //ngx_http_log_phase三個(gè)階段的checker方法? //返回值:?ngx_ok,http框架會(huì)將控制權(quán)交還給epoll模塊? ngx_int_t?ngx_http_core_generic_phase(ngx_http_request_t?*r,ngx_http_phase_handler_t?*ph)? {? ??ngx_int_t?rc;? ??//調(diào)用http模塊的處理方法,這樣這個(gè)http模塊就介入到了這個(gè)請(qǐng)求階段? ??rc?=?ph->handler(r);? ??//跳轉(zhuǎn)到下一個(gè)http階段執(zhí)行? ??if?(rc?==?ngx_ok)????????? ??{? ????r->phase_handler?=?ph->next;? ????return?ngx_again;? ??}? ??? ??//執(zhí)行本階段的下一個(gè)http模塊? ??if?(rc?==?ngx_declined)?????? ??{? ????r->phase_handler++;? ????return?ngx_again;? ??}? ? ??//表示剛執(zhí)行的handler無(wú)法在這一次調(diào)度中處理完這一個(gè)階段,? ??//需要多次調(diào)度才能完成? ??if?(rc?==?ngx_again?||?rc?==?ngx_done)?? ??????????????????????? ??{? ????return?ngx_ok;? ??}? ??//返回出錯(cuò)? ??/*?rc?==?ngx_error?||?rc?==?ngx_http_...?*/? ??ngx_http_finalize_request(r,?rc);? ? ??return?ngx_ok;? }

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊7 分享