欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

nginx處理http請求實例詳解

 更新時間:2017年06月30日 14:31:20   作者:ApeLife  
這篇文章主要介紹了nginx處理http請求實例詳解的相關(guān)資料,需要的朋友可以參考下

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

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

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

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

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

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

//http請求處理讀與寫事件的回調(diào),在ngx_http_process_request函數(shù)中設(shè)置。 
//這個函數(shù)中將會調(diào)用http請求對象的讀寫事件回調(diào)。將event事件模塊與http框架關(guān)聯(lián)起來 
static void ngx_http_request_handler(ngx_event_t *ev) 
{ 
  //如果同時發(fā)生讀寫事件,則只有寫事件才會觸發(fā)。寫事件優(yōu)先級更高 
  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 
  } 
 
  //處理子請求 
  ngx_http_run_posted_requests(c); 
} 

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

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

        

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

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

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

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

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

//調(diào)用各個http模塊協(xié)同處理這個請求, checker函數(shù)內(nèi)部會修改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)用各個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; 
    } 
  } 

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


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


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


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

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

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

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

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

//靜態(tài)模塊將自己注冊到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); 
 
  //靜態(tài)模塊在NGX_HTTP_CONTENT_PHASE階段的處理方法 
  *h = ngx_http_static_handler; 
 
  return NGX_OK; 
} 

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

//訪問權(quán)限模塊將自己注冊到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); 
   
  //訪問權(quán)限模塊在NGX_HTTP_ACCESS_PHASE階段的處理方法 
  *h = ngx_http_access_handler; 
   
  return NGX_OK; 
} 

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

//開始解析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; 
  } 
} 

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


//初始化請求的各個階段 
static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf) 
{ 
  //11個http請求階段,每一個階段都可以有多個http模塊介入。 
  //這里統(tǒng)計11個節(jié)點一共有多個少http模塊。以便下面開辟空間 
  for (i = 0; i < NGX_HTTP_LOG_PHASE; i++)  
  { 
    n += cmcf->phases[i].handlers.nelts; 
  } 
  //開辟空間,存放介入11個處理階段的所有http模塊的回調(diào) 
  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://根據(jù)請求的uri查找location之前,修改請求的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;   //每一個階段的checker回調(diào) 
        break; 
      case NGX_HTTP_FIND_CONFIG_PHASE://根據(jù)請求的uri查找location階段(只能由http框架實現(xiàn)) 
        find_config_index = n; 
        ph->checker = ngx_http_core_find_config_phase; 
        n++; 
        ph++; 
        continue; 
      case NGX_HTTP_REWRITE_PHASE:  //根據(jù)請求的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導(dǎo)致死循環(huán)階段 
        if (use_rewrite)  
        { 
          ph->checker = ngx_http_core_post_rewrite_phase; 
          ph->next = find_config_index;//目的是為了地址重寫后,跳轉(zhuǎn)到NGX_HTTP_FIND_CONFIG_PHASE階段,根據(jù) 
                        //url重寫查找location 
          n++; 
          ph++; 
        } 
        continue; 
      case NGX_HTTP_ACCESS_PHASE:     //是否允許訪問服務(wù)器階段 
        checker = ngx_http_core_access_phase; 
        n++; 
        break; 
      case NGX_HTTP_POST_ACCESS_PHASE:  //根據(jù)NGX_HTTP_ACCESS_PHASE階段的錯誤碼,給客戶端構(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請求內(nèi)容階段,大部分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回調(diào), 
    //但handler回調(diào)每一個模塊自己實現(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個http處理階段中,每一個階段都有一個checker函數(shù),當(dāng)然有些階段的checker函數(shù)是相同的。對每一個處理階段,介入這個階段的所有http模塊都共用同一個checker函數(shù)。這些checker函數(shù)的作用是調(diào)度介入這個階段的所有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框架會將控制權(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模塊的處理方法,這樣這個http模塊就介入到了這個請求階段 
  rc = ph->handler(r); 
  //跳轉(zhuǎn)到下一個http階段執(zhí)行 
  if (rc == NGX_OK)         
  { 
    r->phase_handler = ph->next; 
    return NGX_AGAIN; 
  } 
   
  //執(zhí)行本階段的下一個http模塊 
  if (rc == NGX_DECLINED)      
  { 
    r->phase_handler++; 
    return NGX_AGAIN; 
  } 
 
  //表示剛執(zhí)行的handler無法在這一次調(diào)度中處理完這一個階段, 
  //需要多次調(diào)度才能完成 
  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框架調(diào)度各個http模塊共同完成http請求已經(jīng)分析完了,

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

相關(guān)文章

  • nginx如何配置參數(shù)以及變量

    nginx如何配置參數(shù)以及變量

    這篇文章主要介紹了nginx如何配置參數(shù)以及變量問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 使用Docker主機啟動Nginx服務(wù)器的完整步驟詳解

    使用Docker主機啟動Nginx服務(wù)器的完整步驟詳解

    Docker是一個開源的容器化平臺,用于輕松地打包、部署和運行應(yīng)用程序,而Nginx是一個高性能的開源反向代理服務(wù)器,也是一個流行的Web服務(wù)器,這篇文章主要給大家介紹了關(guān)于使用Docker主機啟動Nginx服務(wù)器的完整步驟,需要的朋友可以參考下
    2024-07-07
  • Apache Nginx 禁止目錄執(zhí)行PHP腳本文件的方法

    Apache Nginx 禁止目錄執(zhí)行PHP腳本文件的方法

    這篇文章主要介紹了Apache Nginx 禁止目錄執(zhí)行PHP腳本文件的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • Nginx配置支持ThinkPHP的PATH_INFO

    Nginx配置支持ThinkPHP的PATH_INFO

    這篇文章主要介紹了Nginx配置支持ThinkPHP的PATH_INFO,本文在Ubuntu的開發(fā)環(huán)境加配置成功,需要的朋友可以參考下
    2015-07-07
  • Nginx服務(wù)器中的GZip配置參數(shù)詳解

    Nginx服務(wù)器中的GZip配置參數(shù)詳解

    這篇文章主要介紹了Nginx服務(wù)器中的GZip配置參數(shù)詳解,即利用GZip來壓縮網(wǎng)站頁面數(shù)據(jù),需要的朋友可以參考下
    2015-08-08
  • 詳解nginx請求頭數(shù)據(jù)讀取流程

    詳解nginx請求頭數(shù)據(jù)讀取流程

    這篇文章主要介紹了詳解nginx請求頭數(shù)據(jù)讀取流程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • nginx搭建文件服務(wù)器(保姆級)

    nginx搭建文件服務(wù)器(保姆級)

    我們在工作過程中,有許多大的鏡像或者安裝包等,搭建一個文件服務(wù)器,可以高效的儲存文件,本文就來介紹一下nginx搭建文件服務(wù)器,感興趣的可以了解一下
    2023-06-06
  • k8s部署nginx訪問Tomcat的實現(xiàn)示例

    k8s部署nginx訪問Tomcat的實現(xiàn)示例

    本文介紹了如何使用Kubernetes部署Nginx,并通過Nginx訪問Tomcat,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-08-08
  • Nginx防御DDOS攻擊的配置方法教程

    Nginx防御DDOS攻擊的配置方法教程

    Nginx是一款輕量級的Web服務(wù)器,由俄羅斯的程序設(shè)計師Igor Sysoev所開發(fā),最初供俄國大型的入口網(wǎng)站及搜尋引Rambler使用。 下面這篇文章主要給大家介紹了關(guān)于Nginx防御DDOS攻擊的配置方法,需要的朋友可以參考下。
    2017-07-07
  • WordPress與Drupal的Nginx配置rewrite重寫規(guī)則示例

    WordPress與Drupal的Nginx配置rewrite重寫規(guī)則示例

    這篇文章主要介紹了WordPress與Drupal的Nginx配置重寫規(guī)則示例,文中介紹的rewrite寫法簡單而突出配置重點,需要的朋友可以參考下
    2016-01-01

最新評論