1. 程式人生 > >Nginx對post請求的優化

Nginx對post請求的優化

在nginx配置檔案中設定:

client_body_buffer_size 256k(足夠大);

client_body_in_single_buffer on;

這樣才能在r->request_body->bufs->buf裡存放完整的request_body,要不然,就依賴資料到達的順序,存放在r->request_body->bufs這個ngx_chain_t這個連結串列裡了,需要遍歷取出。

以下部分是對該內容的詳解:

上節說到nginx核心本身不會主動讀取請求體,這個工作是交給請求處理階段的模組來做,但是nginx核心提供了ngx_http_read_client_request_body()介面來讀取請求體,另外還提供了一個丟棄請求體的介面ngx_http_discard_request_body(),在請求執行的各個階段中,任何一個階段的模組如果對請求體感興趣或者希望丟掉客戶端發過來的請求體,可以分別呼叫這兩個介面來完成。這兩個介面是nginx核心提供的處理請求體的標準介面,如果希望配置檔案中一些請求體相關的指令(比如client_body_in_file_only,client_body_buffer_size等)能夠預期工作,

以及能夠正常使用nginx內建的一些和請求體相關的變數(比如$request_body和$request_body_file),一般來說所有模組都必須呼叫這些介面來完成相應操作,如果需要自定義介面來處理請求體,也應儘量相容nginx預設的行為。

1,讀取請求體
請求體的讀取一般發生在nginx的content handler中,一些nginx內建的模組,比如proxy模組,fastcgi模組,uwsgi模組等,這些模組的行為必須將客戶端過來的請求體(如果有的話)以相應協議完整的轉發到後端服務程序,所有的這些模組都是呼叫了ngx_http_read_client_request_body()介面來完成請求體讀取。值得注意的是這些模組會把客戶端的請求體完整的讀取後才開始往後端轉發資料。

由於記憶體的限制,ngx_http_read_client_request_body()介面讀取的請求體會部分或者全部寫入一個臨時檔案中,根據請求體的大小以及相關的指令配置,請求體可能完整放置在一塊連續記憶體中,也可能分別放置在兩塊不同記憶體中,還可能全部存在一個臨時檔案中,最後還可能一部分在記憶體,剩餘部分在臨時檔案中。下面先介紹一下和這些不同儲存行為相關的指令:

client_body_buffer_size:設定快取請求體的buffer大小,預設為系統頁大小的2倍,當請求體的大小超過此大小時,nginx會把請求體寫入到臨時檔案中。可以根據業務需求設定合適的大小,儘量避免磁碟io操作;
client_body_in_single_buffer:指示是否將請求體完整的儲存在一塊連續的記憶體中,預設為off,如果此指令被設定為on,則nginx會保證請求體在不大於client_body_buffer_size設定的值時,被存放在一塊連續的記憶體中,但超過大小時會被整個寫入一個臨時檔案;
client_body_in_file_only:設定是否總是將請求體儲存在臨時檔案中,預設為off,當此指定被設定為on時,即使客戶端顯示指示了請求體長度為0時,nginx還是會為請求建立一個臨時檔案。

接著介紹ngx_http_read_client_request_body()介面的實現,它的定義如下:
[cpp] 
<span style="font-family:SimSun;font-size:18px;">ngx_int_t  
ngx_http_read_client_request_body(ngx_http_request_t *r,  
ngx_http_client_body_handler_pt post_handler)</span>  

該介面有2個引數,第1個為指向請求結構的指標,第2個為一個函式指標,當請求體讀完時,它會被呼叫。之前也說到根據nginx現有行為,模組邏輯會在請求體讀完後執行,這個回撥函式一般就是模組的邏輯處理函式。ngx_http_read_client_request_body()函式首先將引數r對應的主請求的引用加1,這樣做的目的和該介面被呼叫的上下文有關,一般而言,模組是在content handler中呼叫此介面,一個典型的呼叫如下:
[cpp]
<span style="font-family:SimSun;font-size:18px;">static ngx_int_t  
ngx_http_proxy_handler(ngx_http_request_t *r)  
{  
    ...  
    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);  
    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {  
        return rc;  
    }  
    return NGX_DONE;  
}</span>  
上面的程式碼是在porxy模組的content handler,ngx_http_proxy_handler()中呼叫了ngx_http_read_client_request_body()函式,其中ngx_http_upstream_init()被作為回撥函式傳入進介面中,另外nginx中模組的content handler呼叫的上下文如下:
[cpp] view plain copy
<span style="font-family:SimSun;font-size:18px;">ngx_int_t  
ngx_http_core_content_phase(ngx_http_request_t *r,  
    ngx_http_phase_handler_t *ph)  
{  
    ...  
    if (r->content_handler) {  
        r->write_event_handler = ngx_http_request_empty_handler;  
        ngx_http_finalize_request(r, r->content_handler(r));  
        return NGX_OK;  
    }  
    ...  
}</span>  
上面的程式碼中,content handler呼叫之後,它的返回值作為引數呼叫了ngx_http_finalize_request()函式,在請求體沒有被接收完全時,ngx_http_read_client_request_body()函式返回值為NGX_AGAIN,此時content handler,比如ngx_http_proxy_handler()會返回NGX_DONE,而NGX_DONE作為引數傳給ngx_http_finalize_request()函式會導致主請求的引用計數減1,所以正好抵消了ngx_http_read_client_request_body()函式開頭對主請求計數的加1。

接下來回到ngx_http_read_client_request_body()函式,它會檢查該請求的請求體是否已經被讀取或者被丟棄了,如果是的話,則直接呼叫回撥函式並返回NGX_OK,這裡實際上是為子請求檢查,子請求是nginx中的一個概念,nginx中可以在當前請求中發起另外一個或多個全新的子請求來訪問其他的location,關於子請求的具體介紹會在後面的章節作詳細分析,一般而言子請求不需要自己去讀取請求體。

函式接著呼叫ngx_http_test_expect()檢查客戶端是否傳送了Expect: 100-continue頭,是的話則給客戶端回覆"HTTP/1.1 100 Continue",根據http 1.1協議,客戶端可以傳送一個Expect頭來向伺服器表明期望傳送請求體,伺服器如果允許客戶端傳送請求體,則會回覆"HTTP/1.1 100 Continue",客戶端收到時,才會開始傳送請求體。

接著繼續為接收請求體做準備工作,分配一個ngx_http_request_body_t結構,並儲存在r->request_body,這個結構用來儲存請求體讀取過程用到的快取引用,臨時檔案引用,剩餘請求體大小等資訊,它的定義如下。
[cpp] view plain copy
<span style="font-family:SimSun;font-size:18px;">typedef struct {  
    ngx_temp_file_t                  *temp_file;  
    ngx_chain_t                      *bufs;  
    ngx_buf_t                        *buf;  
    off_t                             rest;  
    ngx_chain_t                      *to_write;  
    ngx_http_client_body_handler_pt   post_handler;  
} ngx_http_request_body_t;</span>  

temp_file: 指向儲存請求體的臨時檔案的指標;
bufs: 指向儲存請求體的連結串列頭;
buf: 指向當前用於儲存請求體的記憶體快取;
rest: 當前剩餘的請求體大小;
post_handler:儲存傳給ngx_http_read_client_request_body()函式的回撥函式。
做好準備工作之後,函式開始檢查請求是否帶有content_length頭,如果沒有該頭或者客戶端傳送了一個值為0的content_length頭,表明沒有請求體,這時直接呼叫回撥函式並返回NGX_OK即可。當然如果client_body_in_file_only指令被設定為on,且content_length為0時,該函式在呼叫回撥函式之前,會建立一個空的臨時檔案。

進入到函式下半部分,表明客戶端請求確實表明了要傳送請求體,該函式會先檢查是否在讀取請求頭時預讀了請求體,這裡的檢查是通過判斷儲存請求頭的快取(r->header_in)中是否還有未處理的資料。如果有預讀資料,則分配一個ngx_buf_t結構,並將r->header_in中的預讀資料儲存在其中,並且如果r->header_in中還有剩餘空間,並且能夠容下剩餘未讀取的請求體,這些空間將被繼續使用,而不用分配新的快取,當然甚至如果請求體已經被整個預讀了,則不需要繼續處理了,此時呼叫回撥函式後返回。

如果沒有預讀資料或者預讀不完整,該函式會分配一塊新的記憶體(除非r->header_in還有足夠的剩餘空間),另外如果request_body_in_single_buf指令被設定為no,則預讀的資料會被拷貝進新開闢的記憶體塊中,真正讀取請求體的操作是在ngx_http_do_read_client_request_body()函式,該函式迴圈的讀取請求體並儲存在快取中,如果快取被寫滿了,其中的資料會被清空並寫回到臨時檔案中。當然這裡有可能不能一次將資料讀到,該函式會掛載讀事件並設定讀事件handler為ngx_http_read_client_request_body_handler,另外nginx核心對兩次請求體的讀事件之間也做了超時設定,client_body_timeout指令可以設定這個超時時間,預設為60s,如果下次讀事件超時了,nginx會返回408給客戶端。

最終讀完請求體後,ngx_http_do_read_client_request_body()會根據配置,將請求體調整到預期的位置(記憶體或者檔案),所有情況下請求體都可以從r->request_body的bufs連結串列得到,該連結串列最多可能有2個節點,每個節點為一個buffer,但是這個buffer的內容可能是儲存在記憶體中,也可能是儲存在磁碟檔案中。另外$request_body變數只在當請求體已經被讀取並且是全部儲存在記憶體中,才能取得相應的資料。


相關推薦

Nginxpost請求優化

在nginx配置檔案中設定: client_body_buffer_size 256k(足夠大); client_body_in_single_buffer on; 這樣才能在r->request_body->bufs->buf裡存放完整的request_body,要不然,就依賴資

Lua獲取NginxPost請求資料並寫入Redis

1.環境安裝 1.lnmp.conf 設定 Enable_Nginx_Lua='y'。然後按通常情況安裝。 2.lnmp ./addons.sh安裝redis,如果連線遠端redis伺服器不用裝。 3.安裝lua ubuntu安裝lua apt-get install lua

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

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

nginx檢視post請求日誌

在http段加上 log_format access '$remote_addr - $remote_user [$time_local] "$request" $status $body_byte

nginx處理post請求之資料轉發

        上一篇文章分析了nginx在處理post請求時,如何啟動upstream這個負載均衡模組。它是一個http框架,由它來排程具體的http模組,例如fastcgi, proxyd反向代理等,這些模組負責將來自客戶端 的請求包頭,請求包體轉為與後端伺服器通訊的格

nginx 設定post請求

由於想在nginx日誌上看到post請求的資訊,做了如下的設定: 首先使用python模擬post請求,發現返回405錯誤,通過查資料,找到通過如下配置可以解決此問題 error_page   405 =200 $uri; 在nginx上新增後重啟,再次請求,可以放回資料,

NGINX反向代理HTML頁面的POST請求返回405狀態碼解決方法

nginx html post 405 http 實現如下:server { listen 80; listen 443 ssl; server_name nirvana.test-a.gogen; ssl_certificate /etc/ng

nginxHTTP的POST請求的Urldecode問題

                問題現象:curl庫的POST請求中的引數被urldecode了,而沒有使用庫的POST請求沒有被urldecode請求。原因:curl庫的POST請求的HTTP頭中有“application/x-www-form-urlencoded”,造成nginx對POST請求引數進行u

nginx rewrite 301 跳轉 post 請求失效問題解決

nginx rewrite 301 跳轉 post 請求失效問題解決upstream gaogd{ server 118.8.8.8:80 weight=2; } server { listen 80; server_name www.gaogd.com

Apache、IIS、Nginx等絕大多數web服務器,都不允許靜態文件響應POST請求

ebr ons auth red stat getmethod eas scope decode 最近調用一個接口,發現httppost請求目標網站會出現405 狀態碼,原因為 Apache、IIS、Nginx等絕大多數web服務器,都不允許靜態文件響應POST請求 所以

Apache、IIS、Nginx等絕大多數web服務器,都不允許靜態文件響應POST請求,否則會返回“HTTP/1.1 405 Method not allowed”錯誤。

.com rewrite requested gin pos 2.0 $2 127.0.0.1 page   例1:用Linux下的curl命令發送POST請求給Apache服務器上的HTML靜態頁 [root@new-host ~]# curl -d 1=1 http:/

nginx發送post請求報405的問題

all ron .com AD get try_files 請求 body span [root@elk-server meeting-order]# curl -X POST https://test.abc.com/ <html> <head>

nginx+lua+redis實現post請求接口之黑名單(一)

實現IP黑名單請求限制一、概述 需求:所有訪問/webapi/**的請求必須是POST請求,而且根據請求參數過濾不符合規則的非法請求(黑名單), 這些請求一律不轉發到後端服務器(Tomcat)實現思路:通過在Nginx上進行訪問限制,通過Lua來靈活實現業務需求,而Redis用於存儲黑名單列表。 二、具體實現

nginx采集post請求日誌

content access .cn lis upstream 日誌 安裝 post方式 size 解決問題 1:業務系統以post方式上發請求日誌,需要對日誌進行采集分析; 解決方法: 1:可以安裝 openresty 版本nginx; 2:可以使

nginx日誌access log可以記錄POST請求的引數值

1)      在nginx日誌access log可以記錄POST請求的引數值 實現程度:日誌中可以顯示POST請求所提交的引數值 問題: 日誌中文顯示十六進位制(在配置檔案中配置中文也無效) 沒有對json資料進行測試,正文型別為:Content-T

nginx模組開發 post請求處理

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

http GET 和 POST 請求的優缺點和誤區 --前端優化

Get和Post在面試中一般都會問到,一般的區別: (1)post更安全(不會作為url的一部分,不會被快取、儲存在伺服器日誌、以及瀏覽器瀏覽記錄中) (2)post傳送的資料更大(get有url長度限制) (3)post能傳送更多的資料型別(get只能傳送ASCII字元)

影響 POST 請求檔案上傳失敗的幾個環節的配置(php + nginx

寫在前面 最近寫一個 php 介面,接受上傳的檔案,發現檔案只要超過 5m 以上就會無響應失敗,最後發現是 shadowsocks 的 timeout 設定問題(我全程開了全域性的 VPN),但一開始並不知曉,把 nginx 和 php 的相關配置都看了個遍,乾脆記錄一下,以後遇到此類問題可以按照這個邏輯順序

JavaWeb之不同Tomcat版本get、post請求,中文亂碼問題

Myeclipse安裝時的前期工作空間的編碼準備,就不說了 Tomcat8 public class dd extends HttpServlet { private static final

Nginx靜態資源POST請求返回405 Not Allowed的解決辦法

今天前端同事反應,有個頁面post請求返回405狀態,get請求則返回資料。 原因是Nginx等絕大多數web伺服器,都不允許靜態檔案響應POST請求。 查看了好多部落格,3種解決辦法中只有最後一種有效,記錄一下。 編譯安裝的nginx原始碼目錄下,src/http/