亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

Nginx 過濾模塊的分析

 

過濾模塊的分析

相關(guān)結(jié)構(gòu)體

ngx_chain_t 結(jié)構(gòu)非常簡單,是一個單向鏈表:

 typedef struct ngx_chain_s ngx_chain_t;
 struct ngx_chain_s {
 ngx_buf_t *buf;
 ngx_chain_t *next;
 };

在過濾模塊中,所有輸出的內(nèi)容都是通過一條單向鏈表所組成。這種單向鏈表的設(shè)計,正好應(yīng)和了 Nginx 流式的輸出模式。每次 Nginx 都是讀到一部分的內(nèi)容,就放到鏈表,然后輸出出去。這種設(shè)計的好處是簡單,非阻塞,但是相應(yīng)的問題就是跨鏈表的內(nèi)容操作非常麻煩,如果需要跨鏈表,很多時候都只能緩存鏈表的內(nèi)容。

單鏈表負(fù)載的就是 ngx_buf_t,這個結(jié)構(gòu)體使用非常廣泛,先讓我們看下該結(jié)構(gòu)體的代碼:

 struct ngx_buf_s {
 u_char *pos; /* 當(dāng)前buffer真實內(nèi)容的起始位置 */
 u_char *last; /* 當(dāng)前buffer真實內(nèi)容的結(jié)束位置 */
 off_t file_pos; /* 在文件中真實內(nèi)容的起始位置 */
 off_t file_last; /* 在文件中真實內(nèi)容的結(jié)束位置 */
 u_char *start; /* buffer內(nèi)存的開始分配的位置 */
 u_char *end; /* buffer內(nèi)存的結(jié)束分配的位置 */
 ngx_buf_tag_t tag; /* buffer屬于哪個模塊的標(biāo)志 */
 ngx_file_t *file; /* buffer所引用的文件 */
 /* 用來引用替換過后的buffer,以便當(dāng)所有buffer輸出以后,
 * 這個影子buffer可以被釋放。
 */
 ngx_buf_t *shadow; 
 /* the buf's content could be changed */
 unsigned temporary:1;
 /*
 * the buf's content is in a memory cache or in a read only memory
 * and must not be changed
 */
 unsigned memory:1;
 /* the buf's content is mmap()ed and must not be changed */
 unsigned mmap:1;
 unsigned recycled:1; /* 內(nèi)存可以被輸出并回收 */
 unsigned in_file:1; /* buffer的內(nèi)容在文件中 */
 /* 馬上全部輸出buffer的內(nèi)容, gzip模塊里面用得比較多 */
 unsigned flush:1;
 /* 基本上是一段輸出鏈的最后一個buffer帶的標(biāo)志,標(biāo)示可以輸出,
 * 有些零長度的buffer也可以置該標(biāo)志
 */
 unsigned sync:1;
 /* 所有請求里面最后一塊buffer,包含子請求 */
 unsigned last_buf:1;
 /* 當(dāng)前請求輸出鏈的最后一塊buffer */
 unsigned last_in_chain:1;
 /* shadow鏈里面的最后buffer,可以釋放buffer了 */
 unsigned last_shadow:1;
 /* 是否是暫存文件 */
 unsigned temp_file:1;
 /* 統(tǒng)計用,表示使用次數(shù) */
 /* STUB */ int num;
 };

一般 buffer 結(jié)構(gòu)體可以表示一塊內(nèi)存,內(nèi)存的起始和結(jié)束地址分別用 start 和 end 表示,pos 和 last 表示實際的內(nèi)容。如果內(nèi)容已經(jīng)處理過了,pos 的位置就可以往后移動。如果讀取到新的內(nèi)容,last 的位置就會往后移動。所以 buffer 可以在多次調(diào)用過程中使用。如果 last 等于 end,就說明這塊內(nèi)存已經(jīng)用完了。如果 pos 等于 last,說明內(nèi)存已經(jīng)處理完了。下面是一個簡單的示意圖,說明 buffer 中指針的用法:

Nginx 過濾模塊的分析

 

響應(yīng)頭過濾函數(shù)

響應(yīng)頭過濾函數(shù)主要的用處就是處理 HTTP 響應(yīng)的頭,可以根據(jù)實際情況對于響應(yīng)頭進(jìn)行修改或者添加刪除。響應(yīng)頭過濾函數(shù)先于響應(yīng)體過濾函數(shù),而且只調(diào)用一次,所以一般可作過濾模塊的初始化工作。

響應(yīng)頭過濾函數(shù)的入口只有一個:

 ngx_int_t
 ngx_http_send_header(ngx_http_request_t *r)
 {
 ...
 return ngx_http_top_header_filter(r);
 }

該函數(shù)向客戶端發(fā)送回復(fù)的時候調(diào)用,然后按前一節(jié)所述的執(zhí)行順序。該函數(shù)的返回值一般是 NGX_OK,NGX_ERROR 和 NGX_AGAIN,分別表示處理成功,失敗和未完成。

你可以把 HTTP 響應(yīng)頭的存儲方式想象成一個 hash 表,在 Nginx 內(nèi)部可以很方便地查找和修改各個響應(yīng)頭部,ngx_http_header_filter_module 過濾模塊把所有的 HTTP 頭組合成一個完整的 buffer,最終 ngx_http_write_filter_module 過濾模塊把 buffer 輸出。

按照前一節(jié)過濾模塊的順序,依次講解如下:

Nginx 過濾模塊的分析

 

響應(yīng)體過濾函數(shù)

響應(yīng)體過濾函數(shù)是過濾響應(yīng)主體的函數(shù)。ngx_http_top_body_filter 這個函數(shù)每個請求可能會被執(zhí)行多次,它的入口函數(shù)是 ngx_http_output_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 可以被一般的靜態(tài)處理模塊調(diào)用,也有可能是在 upstream 模塊里面被調(diào)用,對于整個請求的處理階段來說,他們處于的用處都是一樣的,就是把響應(yīng)內(nèi)容過濾,然后發(fā)給客戶端。

具體模塊的響應(yīng)體過濾函數(shù)的格式類似這樣:

 static int 
 ngx_http_example_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
 ...
 return ngx_http_next_body_filter(r, in);
 }

該函數(shù)的返回值一般是 NGX_OK,NGX_ERROR 和 NGX_AGAIN,分別表示處理成功,失敗和未完成。

主要功能介紹

響應(yīng)的主體內(nèi)容就存于單鏈表 in,鏈表一般不會太長,有時 in 參數(shù)可能為 NULL。in中存有buf結(jié)構(gòu)體中,對于靜態(tài)文件,這個buf大小默認(rèn)是 32K;對于反向代理的應(yīng)用,這個buf可能是4k或者8k。為了保持內(nèi)存的低消耗,Nginx一般不會分配過大的內(nèi)存,處理的原則是收到一定的數(shù)據(jù),就發(fā)送出去。一個簡單的例子,可以看看Nginx的chunked_filter模塊,在沒有 content-length 的情況下,chunk 模塊可以流式(stream)的加上長度,方便瀏覽器接收和顯示內(nèi)容。

在響應(yīng)體過濾模塊中,尤其要注意的是 buf 的標(biāo)志位,完整描述可以在“相關(guān)結(jié)構(gòu)體”這個節(jié)中看到。如果 buf 中包含 last 標(biāo)志,說明是最后一塊 buf,可以直接輸出并結(jié)束請求了。如果有 flush 標(biāo)志,說明這塊 buf 需要馬上輸出,不能緩存。如果整塊 buffer 經(jīng)過處理完以后,沒有數(shù)據(jù)了,你可以把 buffer 的 sync 標(biāo)志置上,表示只是同步的用處。

當(dāng)所有的過濾模塊都處理完畢時,在最后的 write_fitler 模塊中,Nginx 會將 in 輸出鏈拷貝到 r->out 輸出鏈的末尾,然后調(diào)用 sendfile 或者 writev 接口輸出。由于 Nginx 是非阻塞的 socket 接口,寫操作并不一定會成功,可能會有部分?jǐn)?shù)據(jù)還殘存在 r->out。在下次的調(diào)用中,Nginx 會繼續(xù)嘗試發(fā)送,直至成功。

發(fā)出子請求

Nginx 過濾模塊一大特色就是可以發(fā)出子請求,也就是在過濾響應(yīng)內(nèi)容的時候,你可以發(fā)送新的請求,Nginx 會根據(jù)你調(diào)用的先后順序,將多個回復(fù)的內(nèi)容拼接成正常的響應(yīng)主體。一個簡單的例子可以參考 addition 模塊。

Nginx 是如何保證父請求和子請求的順序呢?當(dāng) Nginx 發(fā)出子請求時,就會調(diào)用 ngx_http_subrequest 函數(shù),將子請求插入父請求的 r->postponed 鏈表中。子請求會在主請求執(zhí)行完畢時獲得依次調(diào)用。子請求同樣會有一個請求所有的生存期和處理過程,也會進(jìn)入過濾模塊流程。

關(guān)鍵點是在 postpone_filter 模塊中,它會拼接主請求和子請求的響應(yīng)內(nèi)容。r->postponed 按次序保存有父請求和子請求,它是一個鏈表,如果前面一個請求未完成,那后一個請求內(nèi)容就不會輸出。當(dāng)前一個請求完成時并輸出時,后一個請求才可輸出,當(dāng)所有的子請求都完成時,所有的響應(yīng)內(nèi)容也就輸出完畢了。

一些優(yōu)化措施

Nginx 過濾模塊涉及到的結(jié)構(gòu)體,主要就是 chain 和 buf,非常簡單。在日常的過濾模塊中,這兩類結(jié)構(gòu)使用非常頻繁,Nginx采用類似 freelist 重復(fù)利用的原則,將使用完畢的 chain 或者 buf 結(jié)構(gòu)體,放置到一個固定的空閑鏈表里,以待下次使用。

比如,在通用內(nèi)存池結(jié)構(gòu)體中,pool->chain 變量里面就保存著釋放的 chain。而一般的 buf 結(jié)構(gòu)體,沒有模塊間公用的空閑鏈表池,都是保存在各模塊的緩存空閑鏈表池里面。對于 buf 結(jié)構(gòu)體,還有一種 busy 鏈表,表示該鏈表中的 buf 都處于輸出狀態(tài),如果 buf 輸出完畢,這些 buf 就可以釋放并重復(fù)利用了。

功能函數(shù)名chain 分配ngx_alloc_chain_linkchain 釋放ngx_free_chainbuf 分配ngx_chain_get_free_bufbuf 釋放ngx_chain_update_chains

過濾內(nèi)容的緩存

由于 Nginx 設(shè)計流式的輸出結(jié)構(gòu),當(dāng)我們需要對響應(yīng)內(nèi)容作全文過濾的時候,必須緩存部分的 buf 內(nèi)容。該類過濾模塊往往比較復(fù)雜,比如 sub,ssi,gzip 等模塊。這類模塊的設(shè)計非常靈活,我簡單講一下設(shè)計原則:

  1. 輸入鏈 in 需要拷貝操作,經(jīng)過緩存的過濾模塊,輸入輸出鏈往往已經(jīng)完全不一樣了,所以需要拷貝,通過 ngx_chain_add_copy 函數(shù)完成。
  2. 一般有自己的 free 和 busy 緩存鏈表池,可以提高 buf 分配效率。
  3. 如果需要分配大塊內(nèi)容,一般分配固定大小的內(nèi)存卡,并設(shè)置 recycled 標(biāo)志,表示可以重復(fù)利用。
  4. 原有的輸入 buf 被替換緩存時,必須將其 buf->pos 設(shè)為 buf->last,表明原有的 buf 已經(jīng)被輸出完畢。或者在新建立的 buf,將 buf->shadow 指向舊的 buf,以便輸出完畢時及時釋放舊的 buf。

分享到:
標(biāo)簽:Nginx
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達(dá)人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定