1. 程式人生 > >nginx處理http

nginx處理http

ngx_http_read_client_request_body 

  HTTP包體的長度有可能非常大,如果試圖一次性呼叫並讀取完所有的包體,那麼多半會阻塞Nginx程序。HTTP框架提供了一種方法來非同步地接收包體:

    ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler);
    ngx_http_read_client_request_body是一個非同步方法,呼叫它只是說明要求Nginx開始接收請求的包體,並不表示是否已經接收完,當接收完所有的包體內容後,post_handler指向的回撥方法會被呼叫。因此,即使在呼叫了ngx_http_read_client_request_body方法後它已經返回,也無法確定這時是否已經呼叫過post_handler指向的方法。換句話說,ngx_http_read_client_request_body返回時既有可能已經接收完請求中所有的包體(假如包體的長度很小),也有可能還沒開始接收包體。如果ngx_http_read_client_request_body是在ngx_http_mytest_handler處理方法中呼叫的,那麼後者一般要返回NGX_DONE,因為下一步就是將它的返回值作為引數傳給ngx_http_finalize_request。NGX_DONE的意義如下:
NGX_DONE:表示到此為止,同時HTTP框架將暫時不再繼續執行這個請求的後續部分。事實上,這時會檢查連線的型別,如果是keepalive型別的使用者請求,就會保持住HTTP連線,然後把控制權交給Nginx。這個返回碼很有用,考慮以下場景:在一個請求中我們必須訪問一個耗時極長的操作(比如某個網路呼叫),這樣會阻塞住Nginx,又因為我們沒有把控制權交還給Nginx,而是在ngx_http_mytest_handler中讓Nginx worker程序休眠了(如等待網路的回包),所以,這就會導致Nginx出現效能問題,該程序上的其他使用者請求也得不到響應。可如果我們把這個耗時極長的操作分為上下兩個部分(就像Linux核心中對中斷處理的劃分),上半部分和下半部分都是無阻塞的(耗時很少的操作),這樣,在ngx_http_mytest_handler進入時呼叫上半部分,然後返回NGX_DONE,把控制交還給Nginx,從而讓Nginx繼續處理其他請求。在下半部分被觸發時(這裡不探討具體的實現方式,事實上使用upstream方式做反向代理時用的就是這種思想),再回調下半部分處理方法,這樣就可以保證Nginx的高效能特性了。如果需要徹底瞭解NGX_DONE的意義,那麼必須學習第11章內容,其中還涉及請求的引用計數內容。
    下面看一下包體接收完畢後的回撥方法原型ngx_http_client_body_handler_pt是如何定義的:
    typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);
    其中,有引數ngx_http_request_t *r,這個請求的資訊都可以從r中獲得。這樣可以定義一個方法void func(ngx_http_request_t *r),在Nginx接收完包體時呼叫它,另外,後續的流程也都會寫在這個方法中,例如:
    void ngx_http_mytest_body_handler(ngx_http_request_t *r)
    {
     …
    }
    注意 ngx_http_mytest_body_handler的返回型別是void,Nginx不會根據返回值做一些收尾工作,因此,我們在該方法裡處理完請求時必須要主動呼叫ngx_http_finalize_request方法來結束請求。
    接收包體時可以這樣寫:
            ngx_int_t rc = ngx_http_read_client_request_body(r, ngx_http_mytest_body_handler);

            if (rc >= NGX_http_SPECIAL_RESPONSE) {
                return rc;
            }
            return NGX_DONE;
    Nginx非同步接收HTTP請求的包體的內容將在11.8節中詳述。
    如果不想處理請求中的包體,那麼可以呼叫ngx_http_discard_request_body方法將接收自客戶端的HTTP包體丟棄掉。例如:
    ngx_int_t rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK) {
        return rc;
    }
    ngx_http_discard_request_body只是丟棄包體,不處理包體不就行了嗎?何必還要呼叫ngx_http_discard_request_body方法呢?其實這一步非常有意義,因為有些客戶端可能會一直試圖傳送包體,而如果HTTP模組不接收發來的TCP流,有可能造成客戶端傳送超時。

    接收完請求的包體後,可以在r->request_body->temp_file->file中獲取臨時檔案(假定將r->request_body_in_file_only標誌位設為1,那就一定可以在這個變數獲取到包體。更復雜的接收包體的方式本節暫不討論)。file是一個ngx_file_t型別。這裡,我們可以從r->request_body->temp_file->file.name中獲取Nginx接收到的請求包體所在檔案的名稱(包括路徑)。

返回值的意義

 每個階段都有一個與之相關的handler的列表。一旦把handler註冊到對應的階段,那麼handler就會返回某個下面的值:
NGX_OK:請求已經成功處理,請求將會傳到下一個階段。
NGX_DECLINED:請求需要被轉發到本階段的下一個handler
NGX_AGAIN,NGX_DONE:請求已經被正確處理,同時請求被掛起,直到某個事件(子請求結束、socket可寫或超時等)到來,handler才會再次被呼叫。

//函式功能:解析請求行。
static void ngx_http_process_request_line(ngx_event_t *rev)
{
    。。。
    rc = NGX_AGAIN;
    for ( ;; ) {
        if (rc == NGX_AGAIN) {   // NO.1
            n = ngx_http_read_request_header(r);  
 //recv()讀取請求頭資訊
            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }

        //記錄請求行中的請求方法(Method),請求uri以及http協議版本在緩衝區中的起始位置
        rc = ngx_http_parse_request_line(r, r->header_in);
        if (rc == NGX_OK) {
            …
            c->log->action = "reading client request headers";
            rev->handler = ngx_http_process_request_headers;
            ngx_http_process_request_headers(rev);
            return;
        }
        。。。
    }
}

//函式功能:處理HTTP請求頭。


static void  ngx_http_process_request_headers(ngx_event_t *rev)
{
   。。。
    if (rev->timedout) {  // 是否超時
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        c->timedout = 1;
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }
    。。。
    rc = NGX_AGAIN;
    for ( ;; ) {
        if (rc == NGX_AGAIN) {
            if (r->header_in->pos == r->header_in->end) {  //一般不需要
                rv = ngx_http_alloc_large_header_buffer(r, 0);
                。。。
            }
            n = ngx_http_read_request_header(r);   //快速返回了,因為已經讀過
            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }
        rc = ngx_http_parse_header_line(r, r->header_in,
                                 cscf->underscores_in_headers);  //解析HTTP頭部
        if (rc == NGX_OK) {
            if (r->invalid_header && cscf->ignore_invalid_headers) {
                。。。
            //以key 和value的格式儲存HTTP頭
            h = ngx_list_push(&r->headers_in.headers);   //ngx_http_headers_in_t ,存放key,value
            if (h == NULL) {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }
            h->hash = r->header_hash;
            h->key.len = r->header_name_end - r->header_name_start;
            h->key.data = r->header_name_start; // "User-Agent",host
            h->key.data[h->key.len] = '\0';
            h->value.len = r->header_end - r->header_start;
            h->value.data = r->header_start;     
            。。。
            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {  //檢驗函式
                return;
            }
            continue;
        }
        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
            /* a whole header has been parsed successfully */
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done");
            r->request_length += r->header_in->pos - r->header_in->start;
            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
            rc = ngx_http_process_request_header(r);  //檢驗頭資訊如1.1協議需要host資訊?
            if (rc != NGX_OK) {
                return;
            }
            ngx_http_process_request(r);  
            return;
        }
       。。。。
    }
}

//函式功能:處理請求。

static voidngx_http_process_request(ngx_http_request_t *r)
{
    。。。
    if (c->read->timer_set) {      // 超時,過了一會兒,就在這裡了
        ngx_del_timer(c->read);  //here
    }
    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    r->read_event_handler = ngx_http_block_reading;
    ngx_http_handler(r);  //核心是ngx_http_core_run_phases,進入各階段處理函式
    ngx_http_run_posted_requests(c);
}


相關推薦

nginx處理http(http變數篇)

nginx http變數定義 struct ngx_http_variable_s {     ngx_str_t                     name; &nbs

nginx處理http

ngx_http_read_client_request_body    HTTP包體的長度有可能非常大,如果試圖一次性呼叫並讀取完所有的包體,那麼多半會阻塞Nginx程序。HTTP框架提供了一種方法來非同步地接收包體:     ngx_int_t ngx_http_re

nginx處理http(http處理結構篇)

http頭部結構定義 頭部處理函式指標定義 /* @param r http請求結構 @param h http頭部的hash表 @param offset 位於結構體中的偏移量 */ typedef ngx_int_t (*ngx_http_heade

萬字長文!一次性弄懂 Nginx 處理 HTTP 請求的 11 個階段

# Nginx 處理一個 HTTP 請求的全過程 前面給大家講了 [Nginx 是如何處理 HTTP請求頭部的](https://iziyang.github.io/2020/04/08/4-nginx/),接下來就到了真正處理 HTTP 請求的階段了。先看下面這張圖,這張圖是 Nginx 處理 HTTP

NginxHTTP 499 狀態碼處理

搜索 是什麽 src 多個 客戶端連接 alt nec logs 源碼 1、前言   今天在處理一個客戶問題,遇到Nginx access log中出現大量的499狀態碼。實際場景是:客戶的域名通過cname解析到我們的Nginx反向代理集群上來,客戶的Web服務是由一個

nginx丟棄http包體處理

length 如果 等於 sina 函數 文件 清0 能夠 request http框架丟棄http請求包體和上一篇文章http框架接收包體, 都是由http框架提供的兩個方法,供http各個模塊調用,從而決定對包體做什麽處理。是選擇丟棄還是接收,都是由模塊決定的。例如靜

NginxHTTP請求的11個處理階段

Nginx將一個HTTP請求分成多個階段。以模組為單位進行處理。這樣做的優點是使處理過程更加靈活、減少耦合度。HTTP框架將處理分成了11個階段,各個階段能夠包括隨意多個HTTP模組並以流水線的方式處理請求。這11個HTTP階段例如以下所看到的:

nginx系列7:處理HTTP請求的11個階段

處理HTTP請求的11個階段 如下圖: 序號 階段 指令 備註 1 POST_READ realip 獲取客戶端真實IP 2 SERVER_REWRITE rewrite

菜鳥學習nginxHTTP請求處理(1)

上一篇主要介紹Nginx是如何處理HTTP Header。由於HTTP請求處理這部分程式碼是Nginx核心內容,打算用兩篇文章深入介紹。本篇先介紹HTTP處理的11個階段。 一、HTTP處理11階段 1.1、為什麼要有11階段? 這個得從Nginx設計出發。首先Nginx是單執行緒且

Nginx處理 HTTP 請求

概述         在 Nginx 的初始化啟動過程中,worker 工作程序會呼叫事件模組的ngx_event_process_init 方法為每個監聽套接字ngx_listening_t 分配一個 ngx_connection_t 連線,並設定該連線上讀事件的

nginx處理post請求(http響應頭部的收發)

        上一篇文章分析了nginx如何傳送來自客戶端的請求資料到後端伺服器, 本篇文章開始將分析nginx如何接收來自後端伺服器的響應。nginx接收來自後端伺服器的響應分為兩個過程,一個是接收來自後端伺服器的http響應頭部, 另一個是接收來自後端伺服器的響應包體

NGINXHTTP請求的11個處理階段

Nginx的模組化設計使得每一個HTTP模組可以僅專注於完成一個獨立的、簡單的功能,而一個請求的完整處理過程可以由無數個HTTP模組共同合作完成。這種設計有非常好的簡單性、可測試性、可擴充套件性,然而,當多個http模組流水式地處理同一個請求時,單一的處理順序是無法滿足靈

優化 Nginx 處理事件模型

標準 root 高效 nginx pre conf icop con div Nginx 的連接處理機制在不同的操作系統會采用不同的 I/O 模型,要根據不同的系統選擇不同的事件處理模型,可供選擇的事件處理模型有:kqueue 、rtsig 、epoll 、/dev/pol

(二)servlet處理Http響應

web nbsp 購物車 res 請求 head 自動 地址 發生 重定向:web服務器收到客戶端的請求後,通知客戶端發送一個新的請求到另一個房web服務器。   resp.sendRedirect("URL"); 特點:1)客戶端發送兩次請求 2)瀏

Serlvet 處理http請求並保持長連接

數據 http print htm boa out eth 3.0 cte 一.Servlet,一個請求在容器中是如何處理的 Servlet規定的,相應客戶請求訪問特定Servlet流程如下: 1.客戶端發出請求。 2.Servlet容器接收客戶請求解析。 3.Se

nginx配置http跳轉https

文件 簡單 div 文件頭部 配置 gin list ... 需要 配置相當簡單,在配置文件頭部加一行,如下: server { listen *:80;//監聽80端口 return 301 htt

(轉)關於Tomcat的點點滴滴(體系架構、處理http請求的過程、安裝和配置、目錄結構、設置壓縮和對中文文件名的支持、以及Catalina這個名字的由來……等)

https 設置 重啟 specific 調用 持久化數據 所在 original apps 轉自:http://itfish.net/article/41668.html 總結Tomcat的體系架構、處理http請求的過程、安裝和配置、目錄結構、設置壓縮和對中文文件名

Nginxhttp配置段介紹

ngx_http_core_module1 概述本文將介紹nginx裏http配置段的相關配置,主要介紹ngx_http_core_module這個模塊的相關功能和命令。由於主配置文件的http配置段裏有 include /etc/nginx/conf.d/*.conf; 這個配置,因此可以單獨在路徑/et

java處理HTTP請求

connect implement turn set readline catch append 內容 nco 1 import com.diyfintech.wx.service.HttpService; 2 import org.springframework.s

Spring MVC 處理HTTP請求的整體流程

含義 alt myba 解析 patch ros ati 框架 ice   DispatcherServlet是一個前端控制器,是整個Spring MVC框架的核心組件。它在接收HTTP請求之後,根據請求調用Spring MVC中的各個組件。   常用接口及其含義:   1