麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 網站 > Nginx > 正文

nginx處理http請求實例詳解

2024-08-30 12:29:01
字體:
來源:轉載
供稿:網友

     本文在這基礎上分析nginx服務器收到http請求行、請求頭部后,http框架是如何調度各個http模塊共同完成這個http請求。例如: http框架調度靜態模塊,獲取服務器目錄下的某個html頁面返回給客戶端; 或者http框架調度access權限訪問模塊,判斷這個客戶端是否有權限訪問服務器。

一、event事件與http框架的交互

        在接收完http請求行、http請求頭部后,會調用ngx_http_process_request這個函數開始處理http請求。因為一個http請求由11個處理階段組成,而每一個處理階段都允許多個http模塊介入,因此在這個函數中,將調度各個階段的http模塊共同完成這個請求。

//接收到http請求行與請求頭后,http的處理流程,是第一個http處理請求的讀事件回調 //這個函數執行后,將把讀寫事件的回調設置為ngx_http_request_handler。這樣下次再有事件時 //將調用ngx_http_request_handler函數來處理,而不會再調用ngx_http_process_request了 static void ngx_http_process_request(ngx_http_request_t *r) {   ngx_connection_t *c;   c = r->connection;   //因為已經接收完http請求行、請求頭部了,準備調用各個http模塊處理請求了。   //因此需要接收任何來自客戶端的讀事件,也就不存在接收http請求頭部超時問題   if (c->read->timer_set)    {     ngx_del_timer(c->read);   }   //重新設置當前連接的讀寫事件回調   c->read->handler = ngx_http_request_handler;   c->write->handler = ngx_http_request_handler;   //設置http請求對象的讀事件回調,這個回調不做任何的事情。   //那http請求對象的讀事件回調,與上面的連接對應的讀事件回調有什么關系呢?   //當讀事件發生后,連接對應的讀事件回調ngx_http_request_handler會被調用,   //在這個回調內會調用http請求對象的讀事件回調ngx_http_block_reading,而這個回調是   //不會做任何事件的,因此相當于忽略了讀事件。因為已經接收完了請求行請求頭,現在要做的是調用各個http模塊,   //對接收到的請求行請求頭進行處理   r->read_event_handler = ngx_http_block_reading;    //調用各個http模塊協同處理這個請求   ngx_http_handler(r);   //處理子請求   ngx_http_run_posted_requests(c); }

      ngx_http_process_request函數只會被調用一次。如果一次調度并不能處理完11個http階段,那會將連接對象對應的讀事件、寫事件回調設置為ngx_http_request_handler。而請求對象的讀事件設置為ngx_http_block_reading, 請求對象的寫事件回調設置為ngx_http_core_run_phases, 這個回調在ngx_http_handler內設置。這樣在事件再次到來時不會調用

ngx_http_process_request函數處理了。那event事件模塊的讀寫事件回調與http請求對象的讀寫事件回調有什么關系呢?

nginx,http請求,nginx處理http請求

//http請求處理讀與寫事件的回調,在ngx_http_process_request函數中設置。 //這個函數中將會調用http請求對象的讀寫事件回調。將event事件模塊與http框架關聯起來 static void ngx_http_request_handler(ngx_event_t *ev) {   //如果同時發生讀寫事件,則只有寫事件才會觸發。寫事件優先級更高   if (ev->write)    {     r->write_event_handler(r);  //在函數ngx_http_handler設置為:ngx_http_core_run_phases    }   else   {     r->read_event_handler(r);  //在函數ngx_http_process_request設置為:ngx_http_block_reading   }    //處理子請求   ngx_http_run_posted_requests(c); } 

可以看到,連接對象的讀事件回調中,會調用http請求對象的讀事件回調。連接對象的寫事件回調會調用http請求對象的寫事件回調。

nginx,http請求,nginx處理http請求

        圖中可看出,在event的讀事件發生時,epoll返回后會調用讀事件的回調ngx_http_request_handler。在這個讀事件回調中,又會調用http框架,也就是http請求對象的讀事件回調ngx_http_block_reading,這個http請求對象的讀事件回調是不做任何事情的,相當于忽略讀事件。因此http框架將會返回到事件模塊。那為什么要忽略讀事件呢?因為http請求行、請求頭部都已經全部接收完成了, 現在要做的是調度各個http模塊共同協作,完成對接收到的請求行,請求頭部的處理。因此不需要接收來自客戶端任何數據了。

 

        nginx,http請求,nginx處理http請求

        對于寫事件的處理就復雜多了,  在event的寫事件發生時,epoll返回后會調用寫事件的回調ngx_http_request_handler,在這個寫事件回調中,又會調用http框架,也就是http請求對象的寫事件回調ngx_http_core_run_phases。這個http框架的回調會調度介入11個請求階段的各個http模塊的hander方法,共同完成http請求。

二、調度http模塊處理請求

        在上面代碼中,會調度ngx_http_core_run_phases這個函數,使得各個http模塊能介入到http請求中來。而這個函數是在ngx_http_handler設置的。

//調用各個http模塊協同處理這個請求 void ngx_http_handler(ngx_http_request_t *r) {   //不需要進行內部跳轉。什么是內部跳轉? 例如有個location結構,里面的   //   if (!r->internal)     {     //將數組序號設為0,表示從數組第一個元素開始處理http請求     //這個下標很重要,決定了當前要處理的是11個階段中的哪一個階段,     //以及由這個階段的哪個http模塊處理請求     r->phase_handler = 0;   }    else    {     //需要做內部跳轉     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);     //將序號設置為server_rewrite_index     r->phase_handler = cmcf->phase_engine.server_rewrite_index;   }   //設置請求對象的寫事件回調,這個回調將會調度介入11個http階段的各個http模塊   //共同完成對請求的處理   r->write_event_handler = ngx_http_core_run_phases;   //開始調度介入11個http階段的各個http模塊   ngx_http_core_run_phases(r); } 

而ngx_http_core_run_phases函數就很簡單了,調度介入11個http處理階段的所有http模塊的checker方法。

//調用各個http模塊協同處理這個請求, checker函數內部會修改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;   //調用各個http模塊的checker方法,使得各個http模塊可以介入http請求   while (ph[r->phase_handler].checker)   {     rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);     //從http模塊返回NGX_OK,http框架則會把控制全交還給事件模塊     if (rc == NGX_OK)         {       return;     }   } 

假設階段2有三個http模塊介入了http請求, 階段3有一個模塊介入了http請求、階段4也有一個模塊介入了請求。當開始處理階段2時,將調用階段2中的所有http模塊進行處理,此時phase_handler指向階段2的開始位置。之后每處理完階段2中的一個模塊時,phase_handler指向階段2的下一個模塊,直到階段2處理完成。

nginx,http請求,nginx處理http請求

        當階段2中的所有http模塊都處理完成時,phase_handler將指向階段3

nginx,http請求,nginx處理http請求

        因階段3只有一個http模塊,因此當階段3中的所有http模塊都處理完成時,phase_handler將指向階段4

nginx,http請求,nginx處理http請求

        那這個handlers數組是什么時候創建的呢? 每一個http模塊的checker回調又是做什么呢? 接下來將分析這兩個問題

三、11個http請求階段數組創建

        在解析nginx.conf配置文件時,解析到http塊時,會調用ngx_http_block這個函數開始解析http塊。在這個函數中,也會把所有需要介入到11個http請求階段的http模塊,注冊到數組中。

//開始解析http塊 static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {   //http配置解析完成后的后續處理,使得各個http模塊可以介入到11個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)      {       //每一個http模塊的在這個postconfiguration函數中,都可以把自己注冊到11個http階段       if (module->postconfiguration(cf) != NGX_OK)        {         return NGX_CONF_ERROR;       }     }   } } 

        例如ngx_http_static_module靜態模塊,會將自己介入11個http階段的NGX_HTTP_CONTENT_PHASE階段回調設置為ngx_http_static_handler

//靜態模塊將自己注冊到11個http請求階段中的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);    //靜態模塊在NGX_HTTP_CONTENT_PHASE階段的處理方法   *h = ngx_http_static_handler;    return NGX_OK; } 

例如: ngx_http_access_module訪問權限模塊,會將自己介入11個http階段的NGX_HTTP_ACCESS_PHASE階段回調設置為ngx_http_access_handler

//訪問權限模塊將自己注冊到11個http請求階段中的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);      //訪問權限模塊在NGX_HTTP_ACCESS_PHASE階段的處理方法   *h = ngx_http_access_handler;      return NGX_OK; } 

上面的這些操作,只是把需要介入到11個http階段的http模塊保存到了ngx_http_core_main_conf_t中的phases成員中,并沒有保存到phase_engine中。那什么時候將phases的內容保存到phase_engine中呢?  還是在ngx_http_block函數中完成

//開始解析http塊 static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {     //初始化請求的各個階段   if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK)    {     return NGX_CONF_ERROR;   } } 

假設階段1有一個http模塊介入請求,階段2有三個http模塊介入請求、階段3也有一個http模塊介入請求。則ngx_http_init_phase_handlers這個函數調用后,從ngx_http_phase_t   phases[11]數組轉換到ngx_http_phase_handler_t    handlers數組的過程如下圖所示:

nginx,http請求,nginx處理http請求

//初始化請求的各個階段 static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf) {   //11個http請求階段,每一個階段都可以有多個http模塊介入。   //這里統計11個節點一共有多個少http模塊。以便下面開辟空間   for (i = 0; i < NGX_HTTP_LOG_PHASE; i++)    {     n += cmcf->phases[i].handlers.nelts;   }   //開辟空間,存放介入11個處理階段的所有http模塊的回調   ph = ngx_pcalloc(cf->pool,n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));   cmcf->phase_engine.handlers = ph;   n = 0;    //對于每一個http處理階段,給該階段中所有介入的http模塊賦值   for (i = 0; i < NGX_HTTP_LOG_PHASE; i++)   {     h = cmcf->phases[i].handlers.elts;      switch (i)      {       case NGX_HTTP_SERVER_REWRITE_PHASE://根據請求的uri查找location之前,修改請求的uri階段         if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1)          {           cmcf->phase_engine.server_rewrite_index = n; //重定向模塊在數組中的位置         }         checker = ngx_http_core_rewrite_phase;   //每一個階段的checker回調         break;       case NGX_HTTP_FIND_CONFIG_PHASE://根據請求的uri查找location階段(只能由http框架實現)         find_config_index = n;         ph->checker = ngx_http_core_find_config_phase;         n++;         ph++;         continue;       case NGX_HTTP_REWRITE_PHASE:  //根據請求的rui查找location之后,修改請求的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導致死循環階段         if (use_rewrite)          {           ph->checker = ngx_http_core_post_rewrite_phase;           ph->next = find_config_index;//目的是為了地址重寫后,跳轉到NGX_HTTP_FIND_CONFIG_PHASE階段,根據                         //url重寫查找location           n++;           ph++;         }         continue;       case NGX_HTTP_ACCESS_PHASE:     //是否允許訪問服務器階段         checker = ngx_http_core_access_phase;         n++;         break;       case NGX_HTTP_POST_ACCESS_PHASE:  //根據NGX_HTTP_ACCESS_PHASE階段的錯誤碼,給客戶端構造響應階段         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請求內容階段,大部分http模塊最愿意介入的階段         checker = ngx_http_core_content_phase;         break;       default:         //NGX_HTTP_POST_READ_PHASE,          //NGX_HTTP_PREACCESS_PHASE,          //NGX_HTTP_LOG_PHASE三個階段的checker方法         checker = ngx_http_core_generic_phase;     }     n += cmcf->phases[i].handlers.nelts;     //每一個階段中所介入的所有http模塊,同一個階段中的所有http模塊有唯一的checker回調,     //但handler回調每一個模塊自己實現     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回調

        在11個http處理階段中,每一個階段都有一個checker函數,當然有些階段的checker函數是相同的。對每一個處理階段,介入這個階段的所有http模塊都共用同一個checker函數。這些checker函數的作用是調度介入這個階段的所有http模塊的handler方法,或者切換到一下個http請求階段。下面分析下NGX_HTTP_POST_READ_PHASE,NGX_HTTP_PREACCESS_PHASE,NGX_HTTP_LOG_PHASE三個階段的checker方法。

//NGX_HTTP_POST_READ_PHASE,  //NGX_HTTP_PREACCESS_PHASE,  //NGX_HTTP_LOG_PHASE三個階段的checker方法 //返回值: NGX_OK,http框架會將控制權交還給epoll模塊 ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,ngx_http_phase_handler_t *ph) {   ngx_int_t rc;   //調用http模塊的處理方法,這樣這個http模塊就介入到了這個請求階段   rc = ph->handler(r);   //跳轉到下一個http階段執行   if (rc == NGX_OK)           {     r->phase_handler = ph->next;     return NGX_AGAIN;   }      //執行本階段的下一個http模塊   if (rc == NGX_DECLINED)        {     r->phase_handler++;     return NGX_AGAIN;   }    //表示剛執行的handler無法在這一次調度中處理完這一個階段,   //需要多次調度才能完成   if (rc == NGX_AGAIN || rc == NGX_DONE)                           {     return NGX_OK;   }   //返回出錯   /* rc == NGX_ERROR || rc == NGX_HTTP_... */   ngx_http_finalize_request(r, rc);    return NGX_OK; } 

到此http框架調度各個http模塊共同完成http請求已經分析完了,

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 97干色 | 在线成人免费网站 | 成人视屏在线 | 中国女警察一级毛片视频 | av电影网站在线 | 中午日产幕无线码1区 | 午夜在线视频一区二区三区 | 操碰视频在线观看 | 精品国产一区二区三区蜜殿 | 中文字幕在线视频日本 | 一级做a爱片性色毛片高清 日本一区二区在线看 | 国内精品久久久久久久影视红豆 | 色人阁导航 | 日本看片一区二区三区高清 | 欧美精品一区自拍a毛片在线视频 | 欧美日韩一区,二区,三区,久久精品 | 99re久久最新地址获取 | 久久精品99国产国产精 | 最新毛片在线观看 | 免费国产不卡午夜福在线 | 快播av在线 | 成年人视频在线免费观看 | 黄片毛片一级 | 欧美成人性生活 | xxxxhd73国产 | 欧美成人一区二区三区电影 | 日本黄色大片免费 | 久久网站热最新地址4 | 日日鲁夜夜视频热线播放 | 日韩美香港a一级毛片免费 日韩激情 | 韩国精品视频在线观看 | 欧美一级视频网站 | 二区视频| 国产99免费 | 婷婷亚洲一区二区三区 | 欧美一级做性受免费大片免费 | 韩国19禁在线 | 欧美不卡 | 91精品国产91 | 99精品无人区乱码在线观看 | 欧美成人理论片乱 |