Nginx深入详解之过滤模块
一、模塊簡介
? ? 過濾(filter)模塊是過濾響應頭和內容的模塊,可以對回復的頭和內容進行處理。它的處理時間在獲取回復內容之后,向用戶發送響應之前。它的處理過程分為兩個階段,過濾HTTP回復和頭部和主體,在這兩個階段可以分別對頭部和主體進行修改。下面函數就是分別對頭部和主體進行過濾的函數,所有模塊的響應內容要返回給客戶端,都必須調用這兩個接口:
? ? ngx_http_top_header_filter(r);
? ? ngx_http_top_body_filter(r, in);
二、執行順序
? ? ? 過濾模塊的調用是有順序的,這在編譯的時候就決定了,控制編譯的腳本位于auto/modules中,當編譯完Nginx之后,可以在objs目錄下看到一個ngx_modules.c的文件,打開這個文件能看到如下數據結構:
ngx_module_t *ngx_modules[] = {...&ngx_http_write_filter_module,&ngx_http_header_filter_module,&ngx_http_chunked_filter_module,&ngx_http_range_header_filter_module,&ngx_http_gzip_filter_module,&ngx_http_postpone_filter_module,&ngx_http_ssi_filter_module,&ngx_http_charset_filter_module,&ngx_http_userid_filter_module,&ngx_http_headers_filter_module,&ngx_http_copy_filter_module,&ngx_http_range_body_filter_module,&ngx_http_not_modified_filter_module,NULL };? ? ?從write_filter到not_modified_filter,模塊的執行順序是反向的,即最早執行的是not_modified_filter,然后各個模塊依次執行。所有第三方模塊只能加入到copy_filter和headers_filter模塊之間執行。每個filter模塊的處理函數賦值給全局變量ngx_http_top_header_filter,而前一個filter模塊的處理函數賦值給局部變量ngx_http_next_header_filter,響應頭和響應體過濾函數的執行順序如下圖:
三、模塊編譯
? ? Nginx可以很方便地加入第三方過濾模塊。在過濾模塊的目錄里加入config文件,內容如下:
typedef struct ngx_chain_s ngx_chain_t; struct ngx_chain_s {ngx_buf_t *buf;ngx_chain_t *next; };? ? 一般buffer結構體可以表示一種內存,內存的起始和結束地址分別用start和end表示,pos和last表示實際的內容。如果內容已經處理過了,pos的位置就可以往后移動。如果讀取到新的內容,last的位置就會往后移動。所有buffer可以在多次調用過程中使用。如果last等于end,就說明這塊內存已經用完了。如果pos等于last,說明內存已經處理完了,下面是一個簡單的示意圖,說明buffer中指針的用法:
五、過濾函數
?1、響應頭過濾函數
? ? ? ?響應頭過濾函數的主要用處是處理HTTP響應的頭,可以根據實際情況對于響應頭進行修改或者添加刪除。響應頭過濾函數先于響應體過濾函數,而且只調用一次,所以一般是做過濾模塊的初始化工作,響應頭過濾函數的入口如下:
ngx_int_t ngx_http_send_header(ngx_http_request_t *r) {...return ngx_http_top_header_filter(r); }? ? 該函數在向客戶端發送回復的時候調用,返回值一般為NGX_OK、NGX_ERROR和NGX_AGAIN,分別表示處理成功、失敗和未完成。
? ? ngx_http_header_filter_module過濾模塊把所有的HTTP頭組合成一個完成的buffer,最終由ngx_http_write_filter_module過濾模塊把所有的HTTP頭組合成一個完成的buffer,最終由ngx_http_write_filter_module過濾模塊把buffer輸出。
2、響應體過濾函數
? ? 響應體過濾模塊是過濾響應主題的函數。對于每個請求,函數ngx_http_top_body_filter可能會被執行多次,它的入口函數如下:
ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) {ngx_int_t rc;ngx_connection_t *c;c = r->connection;rc = ngx_http_top_body_filter(r, in);if (rc == NGX_ERROR) {/* NGX_ERROR may be returned by any filter */c->error = 1;}return rc; }? ?對于整個請求的處理階段來說,ngx_http_output_filter的作用就是把響應內存過濾,然后發給客戶端,具體模塊的響應體過濾函數的格式會類似如下:
static int ngx_http_example_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {...return ngx_http_next_body_filter(r, in); }? 該函數的返回值一般是NGX_OK、NGX_ERROR和NGX_AGAIN,分別表示處理成功、失敗和未完成。
? ?響應的主體內容就存于單鏈表in,鏈表一般不會太長,有時in參數可能為NULL。in中存有buf結構體中,對于靜態文件,這個buf大小默認是32K;對于反向代理的應用,這個buf可能是4K或者8K。為了保持內存的低消耗,Nginx一般不會分配過大的內存,處理的原則是收到一定的數據,就發送出去。
? ? 在響應體過濾模塊中,尤其要注意的是buf的標志位,具體可以參看圖2.如果buf中包含last標志,說明是最后一塊buf,可以直接輸出并結束請求了。如果有flush標志,說明這塊buf需要馬上輸出,不能緩存。如果整塊buffer經過處理完以后,沒有數據了,你可以把buffer的sync標志置上,表示只是同步的用處。當所有的過濾模塊都處理完畢時,在最后的write_filter模塊中,Nginx會將in輸出鏈拷貝到r->out輸出鏈的末尾,然后調用sendfile或者writev接口輸出。由于Nginx是非阻塞的socket接口,寫操作并不一定會成功,可能會有部分數據還殘存在r->out。在下次的調用中,Nginx會繼續嘗試發送,直至成功。
六、子請求
? ? Nginx過濾模塊一大特色就是可以發出子請求,即在過濾響應內容的時候,你可以發送新的請求,Nginx會根據你調用的先后順序,將多個回復的內容拼接成正常的響應主體。一個簡單的例子可以參考addtion模塊。當Nginx發出子請求時,就會調用ngx_http_subrequest函數,將子請求插入福請求的r->postponed鏈表中。子請求會在主請求執行完畢時獲得依次調用。子請求同樣會有一個請求所有的生存期和處理過程,也會進入過濾模塊流程。
? ? 關鍵點是在postpone_filter模塊中,它會拼接主請求和子請求的響應內容。r->postponed按次序保存有父請求和子請求,它是一個鏈表,如果前面一個請求未完成,那么后一個請求內容就不會輸出。當前一個請求完成時并輸出時,后一個請求才可輸出,當所有的子請求都完成時,所有的響應內容也就輸出完畢了。
總結
以上是生活随笔為你收集整理的Nginx深入详解之过滤模块的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Nginx-Lua模块的执行顺序
- 下一篇: Nginx深入详解之模块化体系结构