日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

过滤模块

發(fā)布時(shí)間:2024/1/23 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 过滤模块 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、過濾模塊簡介

1.1 執(zhí)行時(shí)間和內(nèi)容

? ? 過濾(filter)模塊是過濾響應(yīng)頭和內(nèi)容的模塊,可以對回復(fù)的頭和內(nèi)容進(jìn)行處理。它的處理時(shí)間在獲取回復(fù)內(nèi)容之后,向用戶發(fā)送響應(yīng)之前。它的處理過程分為兩個(gè)階段,過濾HTTP回復(fù)的頭部和主體,在這兩個(gè)階段可以分別對頭部的主體進(jìn)行修改。

在代碼中有類似的函數(shù):

ngx_http_top_header_filter(r); ngx_http_top_body_filter(r, in);

就是分別對頭部和主體進(jìn)行過濾的函數(shù)。所有模塊的響應(yīng)內(nèi)容要返回給客戶端嗎,都必須調(diào)用這兩個(gè)接口。

1.2 執(zhí)行順序

? ? ?過濾模塊的調(diào)用時(shí)有順序的,它的順序在編譯的時(shí)候就決定了。控制編譯的腳本位于auto/modules中,當(dāng)你編譯完Nginx以后,可以在objs目錄下面看到一個(gè)ngx_modules.c的文件。打開這個(gè)文件,有類似的代碼:

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,模塊的執(zhí)行順序是反向的。也就是說最早執(zhí)行的是not_modified_filter,然后各個(gè)模塊依次執(zhí)行。所有第三方的模塊只能加入到copy_filter和headers_filter模塊之間執(zhí)行。

Nginx執(zhí)行的時(shí)候是怎么按照次序依次來執(zhí)行各個(gè)過濾模塊呢?它采用了一種很隱晦的方法,即通過局部的全局變量。比如,在每個(gè)filter模塊,很可能看到如下代碼:

static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter;... ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_example_header_filter;ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_example_body_filter;

ngx_http_top_header_filter是一個(gè)全局變量。當(dāng)編譯進(jìn)一個(gè)filter模塊的時(shí)候,就被賦值為當(dāng)前filter模塊的處理函數(shù)。而ngx_http_next_header_filter是一個(gè)局部全局變量,它保存了編譯前上一個(gè)filter模塊的處理函數(shù)。所以整體看來,就像用全局變量組成的一條單向鏈表。

每個(gè)模塊想執(zhí)行下一個(gè)過濾函數(shù),只要調(diào)用一下ngx_http_next_header_filter這個(gè)局部變量。而整個(gè)過濾模塊鏈的入口,需要調(diào)用ngx_http_top_header_filter這個(gè)全局變量。

ngx_http_top_body_filter的行為與header filter類似。

響應(yīng)體和響應(yīng)體過濾函數(shù)的執(zhí)行順序如下所示:

這圖只表示了head_filter和body_filter之間的執(zhí)行順序,在header_filter和body_filter處理函數(shù)之間,在body_filter處理函數(shù)之間,可能還有其他執(zhí)行代碼。

1.3 模塊編譯

?Nginx可以方便的加入第三方的過濾模塊。在過濾模塊的目錄里,首先需要加入config文件,文件的內(nèi)容如下:

ngx_addon_name=ngx_http_example_filter_module HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_example_filter_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_example_filter_module.c"

說明把這個(gè)名為ngx_http_example_filter_module的過濾模塊加入,ngx_http_example_filter_module.c是該模塊的源代碼。

注意HTTP_AUX_FILTER_MODULE這個(gè)變量與一般的內(nèi)容處理模塊不同。

二、過濾模塊的分析

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

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

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è)計(jì)的好處是簡單,非阻塞,但是相應(yīng)的問題就是跨鏈表的內(nèi)容操作非常麻煩,如果需要跨鏈表,很多時(shí)候都只能緩存鏈表的內(nèi)容。

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

struct ngx_buf_s {u_char *pos; /* 當(dāng)前buffer真實(shí)內(nèi)容的起始位置 */u_char *last; /* 當(dāng)前buffer真實(shí)內(nèi)容的結(jié)束位置 */off_t file_pos; /* 在文件中真實(shí)內(nèi)容的起始位置 */off_t file_last; /* 在文件中真實(shí)內(nèi)容的結(jié)束位置 */u_char *start; /* buffer內(nèi)存的開始分配的位置 */u_char *end; /* buffer內(nèi)存的結(jié)束分配的位置 */ngx_buf_tag_t tag; /* buffer屬于哪個(gè)模塊的標(biāo)志 */ngx_file_t *file; /* buffer所引用的文件 *//* 用來引用替換過后的buffer,以便當(dāng)所有buffer輸出以后,* 這個(gè)影子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;/* 基本上是一段輸出鏈的最后一個(gè)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)計(jì)用,表示使用次數(shù) *//* STUB */ int num; };

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

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

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

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

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

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

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

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

filter moduledescription
ngx_http_not_modified_filter_module默認(rèn)打開,如果請求的if-modified-since等于回復(fù)的last-modified間值,說明回復(fù)沒有變化,清空所有回復(fù)的內(nèi)容,返回304。
ngx_http_range_body_filter_module默認(rèn)打開,只是響應(yīng)體過濾函數(shù),支持range功能,如果請求包含range請求,那就只發(fā)送range請求的一段內(nèi)容。
ngx_http_copy_filter_module始終打開,只是響應(yīng)體過濾函數(shù), 主要工作是把文件中內(nèi)容讀到內(nèi)存中,以便進(jìn)行處理。
ngx_http_headers_filter_module始終打開,可以設(shè)置expire和Cache-control頭,可以添加任意名稱的頭
ngx_http_userid_filter_module默認(rèn)關(guān)閉,可以添加統(tǒng)計(jì)用的識(shí)別用戶的cookie。
ngx_http_charset_filter_module默認(rèn)關(guān)閉,可以添加charset,也可以將內(nèi)容從一種字符集轉(zhuǎn)換到另外一種字符集,不支持多字節(jié)字符集。
ngx_http_ssi_filter_module默認(rèn)關(guān)閉,過濾SSI請求,可以發(fā)起子請求,去獲取include進(jìn)來的文件
ngx_http_postpone_filter_module始終打開,用來將子請求和主請求的輸出鏈合并
ngx_http_gzip_filter_module默認(rèn)關(guān)閉,支持流式的壓縮內(nèi)容
ngx_http_range_header_filter_module默認(rèn)打開,只是響應(yīng)頭過濾函數(shù),用來解析range頭,并產(chǎn)生range響應(yīng)的頭。
ngx_http_chunked_filter_module默認(rèn)打開,對于HTTP/1.1和缺少content-length的回復(fù)自動(dòng)打開。
ngx_http_header_filter_module始終打開,用來將所有header組成一個(gè)完整的HTTP頭。
ngx_http_write_filter_module始終打開,將輸出鏈拷貝到r->out中,然后輸出內(nèi)容。

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

響應(yīng)體過濾函數(shù)是過濾響應(yīng)主體的函數(shù)。ngx_http_top_body_filter這個(gè)函數(shù)每個(gè)請求可能會(huì)被執(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)用,對于整個(gè)請求的處理階段來說,他們處于的用處都是一樣的,就是把響應(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,分別表示處理成功,失敗和未完成。

4.1 主要功能介紹?

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

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

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

4.2 發(fā)出子請求

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

Nginx是如何保證父請求和子請求的順序呢?當(dāng)Nginx發(fā)出子請求時(shí),就會(huì)調(diào)用ngx_http_subrequest函數(shù),

將子請求插入父請求的r->postponed鏈表中。子請求會(huì)在主請求執(zhí)行完畢時(shí)獲得依次調(diào)用。子請求同樣會(huì)有一個(gè)請求所有的生存期和處理過程,也會(huì)進(jìn)入過濾模塊流程。

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

4.3 一些優(yōu)化措施

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

比如,在通用內(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_link
chain釋放ngx_free_chain
buf分配ngx_chain_get_free_buf
buf釋放ngx_chain_update_chains

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

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

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

    總結(jié)

    以上是生活随笔為你收集整理的过滤模块的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。