<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    nginx請求體讀取(二)

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    2,丟棄請求體

    一個模塊想要主動的丟棄客戶端發過的請求體,可以調用nginx核心提供的ngx_http_discard_request_body()接口,主動丟棄的原因可能有很多種,如模塊的業務邏輯壓根不需要請求體 ,客戶端發送了過大的請求體,另外為了兼容http1.1協議的pipeline請求,模塊有義務主動丟棄不需要的請求體。總之為了保持良好的客戶端兼容性,nginx必須主動丟棄無用的請求體。下面開始分析ngx_http_discard_request_body()函數:

     

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">ngx_int_t  
    2. ngx_http_discard_request_body(ngx_http_request_t *r)  
    3. {  
    4.     ssize_t       size;  
    5.     ngx_event_t  *rev;  
    6.   
    7.     if (r != r->main || r->discard_body) {  
    8.         return NGX_OK;  
    9.     }  
    10.   
    11.     if (ngx_http_test_expect(r) != NGX_OK) {  
    12.         return NGX_HTTP_INTERNAL_SERVER_ERROR;  
    13.     }  
    14.   
    15.     rev = r->connection->read;  
    16.   
    17.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");  
    18.   
    19.     if (rev->timer_set) {  
    20.         ngx_del_timer(rev);  
    21.     }  
    22.   
    23.     if (r->headers_in.content_length_n <= 0 || r->request_body) {  
    24.         return NGX_OK;  
    25.     }  
    26.   
    27.     size = r->header_in->last - r->header_in->pos;  
    28.   
    29.     if (size) {  
    30.         if (r->headers_in.content_length_n > size) {  
    31.             r->header_in->pos += size;  
    32.             r->headers_in.content_length_n -= size;  
    33.   
    34.         } else {  
    35.             r->header_in->pos += (size_t) r->headers_in.content_length_n;  
    36.             r->headers_in.content_length_n = 0;  
    37.             return NGX_OK;  
    38.         }  
    39.     }  
    40.   
    41.     r->read_event_handler = ngx_http_discarded_request_body_handler;  
    42.   
    43.     if (ngx_handle_read_event(rev, 0) != NGX_OK) {  
    44.         return NGX_HTTP_INTERNAL_SERVER_ERROR;  
    45.     }  
    46.   
    47.     if (ngx_http_read_discarded_request_body(r) == NGX_OK) {  
    48.         r->lingering_close = 0;  
    49.   
    50.     } else {  
    51.         r->count++;  
    52.         r->discard_body = 1;  
    53.     }  
    54.   
    55.     return NGX_OK;  
    56. }  
    57. </span>  


    由于函數不長,這里把它完整的列出來了,函數的開始同樣先判斷了不需要再做處理的情況:子請求不需要處理,已經調用過此函數的也不需要再處理。接著調用ngx_http_test_expect() 處理http1.1 expect的情況,根據http1.1的expect機制,如果客戶端發送了expect頭,而服務端不希望接收請求體時,必須返回417(Expectation Failed)錯誤。nginx并沒有這樣做,它只是簡單的讓客戶端把請求體發送過來,然后丟棄掉。接下來,函數刪掉了讀事件上的定時器,因為這時本身就不需要請求體,所以也無所謂客戶端發送的快還是慢了,當然后面還會將到,當nginx已經處理完該請求但客戶端還沒有發送完無用的請求體時,nginx會在讀事件上再掛上定時器。

     

    函數同樣還會檢查請求頭中的content-length頭,客戶端如果打算發送請求體,就必須發送content-length頭,同時還會查看其他地方是不是已經讀取了請求體。如果確實有待處理的請求體,函數接著檢查請求頭buffer中預讀的數據,預讀的數據會直接被丟掉,當然如果請求體已經被全部預讀,函數就直接返回了。

    接下來,如果還有剩余的請求體未處理,該函數調用ngx_handle_read_event()在事件處理機制中掛載好讀事件,并把讀事件的處理函數設置為ngx_http_discarded_request_body_handler。做好這些準備之后,該函數最后調用ngx_http_read_discarded_request_body()接口讀取客戶端過來的請求體并丟棄。如果客戶端并沒有一次將請求體發過來,函數會返回,剩余的數據等到下一次讀事件過來時,交給ngx_http_discarded_request_body_handler()來處理,這時,請求的discard_body將被設置為1用來標識這種情況。另外請求的引用數(count)也被加1,這樣做的目的是客戶端可能在nginx處理完請求之后仍未完整發送待發送的請求體,增加引用是防止nginx核心在處理完請求后直接釋放了請求的相關資源。

    ngx_http_read_discarded_request_body()函數非常簡單,它循環的從鏈接中讀取數據并丟棄,直到讀完接收緩沖區的所有數據,如果請求體已經被讀完了,該函數會設置讀事件的處理函數為ngx_http_block_reading,這個函數僅僅刪除水平觸發的讀事件,防止同一事件不斷被觸發。

    再來看一下讀事件的處理函數ngx_http_discarded_request_body_handler,這個函數每次讀事件來時會被調用,先看一下它的源碼:

     

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">void  
    2. ngx_http_discarded_request_body_handler(ngx_http_request_t *r)  
    3. {  
    4.     ...  
    5.   
    6.     c = r->connection;  
    7.     rev = c->read;  
    8.   
    9.     if (rev->timedout) {  
    10.         c->timedout = 1;  
    11.         c->error = 1;  
    12.         ngx_http_finalize_request(r, NGX_ERROR);  
    13.         return;  
    14.     }  
    15.   
    16.     if (r->lingering_time) {  
    17.         timer = (ngx_msec_t) (r->lingering_time - ngx_time());  
    18.   
    19.         if (timer <= 0) {  
    20.             r->discard_body = 0;  
    21.             r->lingering_close = 0;  
    22.             ngx_http_finalize_request(r, NGX_ERROR);  
    23.             return;  
    24.         }  
    25.   
    26.     } else {  
    27.         timer = 0;  
    28.     }  
    29.   
    30.     rc = ngx_http_read_discarded_request_body(r);  
    31.   
    32.     if (rc == NGX_OK) {  
    33.         r->discard_body = 0;  
    34.         r->lingering_close = 0;  
    35.         ngx_http_finalize_request(r, NGX_DONE);  
    36.         return;  
    37.     }  
    38.   
    39.     /* rc == NGX_AGAIN */  
    40.   
    41.     if (ngx_handle_read_event(rev, 0) != NGX_OK) {  
    42.         c->error = 1;  
    43.         ngx_http_finalize_request(r, NGX_ERROR);  
    44.         return;  
    45.     }  
    46.   
    47.     if (timer) {  
    48.   
    49.         clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);  
    50.   
    51.         timer *= 1000;  
    52.   
    53.         if (timer > clcf->lingering_timeout) {  
    54.             timer = clcf->lingering_timeout;  
    55.         }  
    56.   
    57.         ngx_add_timer(rev, timer);  
    58.     }  
    59. }  
    60. </span>  

    函數一開始就處理了讀事件超時的情況,之前說到在ngx_http_discard_request_body()函數中已經刪除了讀事件的定時器,那么什么時候會設置定時器呢?答案就是在nginx已經處理完該請求,但是又沒有完全將該請求的請求體丟棄的時候(客戶端可能還沒有發送過來),在ngx_http_finalize_connection()函數中,如果檢查到還有未丟棄的請求體時,nginx會添加一個讀事件定時器,它的時長為lingering_timeout指令所指定,默認為5秒,不過這個時間僅僅兩次讀事件之間的超時時間,等待請求體的總時長為lingering_time指令所指定,默認為30秒。這種情況中,該函數如果檢測到超時事件則直接返回并斷開連接。同樣,還需要控制整個丟棄請求體的時長不能超過lingering_time設置的時間,如果超過了最大時長,也會直接返回并斷開連接。

     

    如果讀事件發生在請求處理完之前,則不用處理超時事件,也不用設置定時器,函數只是簡單的調用ngx_http_read_discarded_request_body()來讀取并丟棄數據。

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全