目標(biāo):在nginx層面增加一個(gè)模塊對(duì)請(qǐng)求進(jìn)行處理(例如:給每個(gè)請(qǐng)求生成一個(gè)ID产场,生成ID之后鹅髓,通過(guò)PHP語(yǔ)言可以獲取到該ID)
簡(jiǎn)介:
1、NGINX對(duì)一個(gè)請(qǐng)求的處理過(guò)程
NGINX將http請(qǐng)求劃分為11個(gè)處理階段
階段 | 說(shuō)明 |
---|---|
NGX_HTTP_POST_READ_PHASE | 接收到完整的HTTP頭部后處理的階段 |
NGX_HTTP_SERVER_REWRITE_PHASE | 請(qǐng)求的URI與nginx.conf中的location表達(dá)式匹配前京景,修改請(qǐng)求的URI是一個(gè)獨(dú)立的http階段(重定向) |
NGX_HTTP_FIND_CONFIG_PHASE | 根據(jù)請(qǐng)求的URI尋找匹配的location表達(dá)式窿冯,該階段只能由ngx_http_core_module模塊實(shí)現(xiàn),不建議其他http模塊重新定義這一階段的行為 |
NGX_HTTP_REWRITE_PHASE | 根據(jù)上一階段找到的匹配的location表達(dá)式修改URI |
NGX_HTTP_POST_REWRITE_PHASE | 防止由于nginx.conf配置錯(cuò)誤導(dǎo)致的重寫(xiě)URL后的死循環(huán)确徙,防止死循環(huán)的處理方法:一個(gè)請(qǐng)求重寫(xiě)URL的次數(shù)達(dá)到10次就認(rèn)為死循環(huán)醒串,nginx會(huì)返回500錯(cuò)誤 |
NGX_HTTP_PREACCESS_PHASE | NGX_HTTP_ACCESS_PHASE階段決定訪問(wèn)權(quán)限前执桌,HTTP模塊 可以介入的階段 |
NGX_HTTP_ACCESS_PHASE | 讓HTTP模塊判斷是否允許這個(gè)請(qǐng)求訪問(wèn)nginx服務(wù)器 |
NGX_HTTP_POST_ACCESS_PHASE | 給上一階段收尾,如果NGX_HTTP_ACCESS_PHASE階段的HTTP模塊的handler函數(shù)返回請(qǐng)求沒(méi)有權(quán)限訪問(wèn)(NGX_HTTP_FORBIDEN或者NGX_HTTP_UNAUTHORIZED)厦凤,該階段向用戶(hù)發(fā)送拒絕服務(wù)的錯(cuò)誤碼 |
NGX_HTTP_TRY_FILES_PHASE | 該階段為了nginx中的try_files配置項(xiàng)而設(shè)置鼻吮,當(dāng)HTTP訪問(wèn)靜態(tài)資源時(shí),try_files配置項(xiàng)可以使請(qǐng)求順序的訪問(wèn)多個(gè)靜態(tài)資源较鼓,如果某一次訪問(wèn)失敗椎木,繼續(xù)訪問(wèn)下一靜態(tài)資源。這個(gè)功能是在該階段中實(shí)現(xiàn)的博烂。 |
NGX_HTTP_CONTENT_PHASE | 處理HTTP請(qǐng)求內(nèi)容的階段香椎,該階段是大部分HTTP模塊最愿意介入的階段 |
NGX_HTTP_LOG_PHASE | 處理請(qǐng)求完畢后記錄請(qǐng)求日志的階段。ngx_http_log_module模塊在這一階段加入一個(gè)handler方法禽篱,每個(gè)請(qǐng)求 處理完畢后記錄access_log日志 |
自定義HTTP模塊無(wú)法介入的階段:
NGX_HTTP_FIND_CONFIG_PHASE
NGX_HTTP_POST_REWRITE_PHASE
NGX_HTTP_POST_ACCESS_PHASE
NGX_HTTP_TRY_FILES_PHASE
剩余7個(gè)模塊均可以介入畜伐,每個(gè)階段可以介入模塊的個(gè)數(shù)也是沒(méi)有限制的,多個(gè)模塊可以介入同一階段處理同一請(qǐng)求躺率。
下面記錄的模塊介入的階段:NGX_HTTP_CONTENT_PHASE(處理HTTP請(qǐng)求內(nèi)容)
介入NGX_HTTP_CONTENT_PHASE的方法有兩種玛界,一種介入方法是處理所有請(qǐng)求,一種介入方法是只處理特定的請(qǐng)求悼吱。
下面的介入方法是處理所有請(qǐng)求慎框。
NGINX中關(guān)于HTTP階段的一些處理方法見(jiàn)下一篇(鏈接)
1、增加編譯的配置文件(將自定義HTTP模塊編譯進(jìn)nginx)
自定義模塊的一般目錄結(jié)構(gòu):
+ ngx_http_xxxxxx_module
-config
-ngx_http_xxxxxx_module.c
config文件:編譯nginx可執(zhí)行文件時(shí)后添,會(huì)讀取該文件獲取模塊相關(guān)路徑及模塊名稱(chēng)等
ngx_http_xxxxxx_module.c :處理http請(qǐng)求的代碼文件
舉例:
ngx_addon_name=ngx_http_xxxxxx_module
HTTP_MODULES="$HTTP_MODULES ngx_http_xxxxxx_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_xxxxxx_module.c"
// ngx_addon_name:模塊名稱(chēng)
// HTTP_MODULES:所有的http處理模塊笨枯,自定義模塊前一定要加上$HTTP_MODULES,該變量表示已經(jīng)存在的http模塊
// NGX_ADDON_SRCS:模塊源文件遇西,$NGX_ADDON_SRCS表示已有的模塊源文件路徑馅精,$ngx_addon_dir表示使用.configure命令時(shí)輸入的路徑
// 編譯命令:
// .configure --prefix=/usr/local/adinf/nginx-1.10.3 --with-stream --with-stream_ssl_module --with-http_ssl_module --with-threads --add-module=/data1/htdocs/ngx_http_xxxxxx_module
2、ngx_http_xxxxxx_module.c部分
簡(jiǎn)介:
nginx中的模塊分類(lèi)幾類(lèi):核心模塊粱檀,事件模塊洲敢,http模塊,upstream模塊茄蚯,郵件模塊等
核心模塊
typedef struct {
ngx_str_t xxxxxx_config_value;
} ngx_http_xxxxxx_conf_t;
static ngx_command_t ngx_http_xxxxxx_commands[] = {
{
ngx_string("xxxxxx"), // nginx.conf中沦疾,該模塊會(huì)處理的配置項(xiàng)名稱(chēng)
NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, // 該名稱(chēng)的配置項(xiàng)在conf中允許的配置方式;NGX_HTTP_SRV_CONF表示允許在server塊下出現(xiàn)第队,NGX_CONF_TAKE1表示配置項(xiàng)名稱(chēng)后可以跟一個(gè)參數(shù)
ngx_http_xxxxxx_handler_conf, //解析配置該配置項(xiàng)對(duì)應(yīng)的方法
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_xxxxxx_conf_t, xxxxxx),//該配置項(xiàng)對(duì)應(yīng)的參數(shù)會(huì)被保存到ngx_http_xxxxxx_conf_t的成員xxxxxx中
NULL
},
ngx_null_command
};
static ngx_http_module_t ngx_http_xxxxxx_module_ctx = {
ngx_http_xxxxxx_add_variable, /* preconfiguration */
ngx_http_xxxxxx_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
ngx_http_xxxxxx_create_srv_conf, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_xxxxxx_create_loc_conf, /* create location configuration */
ngx_http_xxxxxx_merge_loc_conf, /* merge location configuration */
};
ngx_module_t ngx_http_xxxxxx_module = {
NGX_MODULE_V1,
&ngx_http_xxxxxx_module_ctx, /* module context */
ngx_http_xxxxxx_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_http_xxxxxx_module:
nginx中有一個(gè)模塊的數(shù)據(jù)結(jié)構(gòu)ngx_module_t
,所有的nginx模塊都要遵循ngx_module_t
的定義刨秆。nginx啟動(dòng)時(shí)會(huì)調(diào)用模塊中定義的方法凳谦。ngx_module_t
中最重要的是要設(shè)置ctx
和commands
兩個(gè)成員。ctx
用于指向一類(lèi) 模塊的上下文衡未,http類(lèi)模塊中需要指向ngx_http_module_t
結(jié)構(gòu)體尸执。commands將處理nginx.conf中的配置項(xiàng)家凯。
NGX_HTTP_MODULE表示該模塊為http模塊。
NGX_MODULE_V1
和NGX_MODULE_V1_PADDING
兩個(gè)宏定義模塊中未用到的成員的初始值如失。
上面的定義中ctx指向ngx_http_xxxxxx_module_ctx
,commands設(shè)置為ngx_http_xxxxxx_commands
绊诲。
ngx_http_xxxxxx_module_ctx:
ngx_http_module_t結(jié)構(gòu)體定義了8個(gè)階段,nginx啟動(dòng)過(guò)程中褪贵,會(huì)調(diào)用結(jié)構(gòu)體定義的相關(guān)方法掂之。
調(diào)用順序:
階段 | 說(shuō)明 |
---|---|
create main configuration | 創(chuàng)建存儲(chǔ)main級(jí)別直屬于http塊的結(jié)構(gòu)體 |
create server configuration | 創(chuàng)建存儲(chǔ)conf中直屬于server塊的配置的結(jié)構(gòu)體 |
create location configuration | 創(chuàng)建存conf中直屬于location塊的配置的結(jié)構(gòu)體 |
preconfiguration | 解析配置文件前 |
init main configuration | 初始化main級(jí)別配置項(xiàng) |
merge server configuration | 合并main級(jí)別和server級(jí)別下同名的配置項(xiàng) |
merge location configuration | 合并server級(jí)別和location級(jí)別下的同名配置項(xiàng) |
postconfiguration | 完成配置項(xiàng)解析 |
示例代碼中
方法ngx_http_xxxxxx_add_variable
在解析配置文件前創(chuàng)建一個(gè)了一個(gè)變量,具體方法實(shí)現(xiàn)看后續(xù)代碼
方法ngx_http_xxxxxx_init
則設(shè)置了NGX_HTTP_CONTENT_PHASE階段的處理http請(qǐng)求的方法脆丁,具體方法實(shí)現(xiàn)看后續(xù)代碼
ngx_http_xxxxxx_commands:解析nginx.conf
// ngx系統(tǒng)方法解析配置項(xiàng)
static char * ngx_http_xxxxxx_handler_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_conf_set_str_slot(cf, cmd, conf); //調(diào)用ngx_conf_set_str_slot 處理ngx_str_t類(lèi)型的變量
return NGX_CONF_OK;
}
ngx_http_xxxxxx_handler_conf方法解析nginx.conf配置世舰,會(huì)將config中配置的值解析到結(jié)構(gòu)體ngx_http_xxxxxx_conf_t中。該方法在nginx啟動(dòng)過(guò)程中被調(diào)用槽卫。
static ngx_str_t xxxxxx_variable = ngx_string("xxxxxx_variable"); // 要添加的變量名
// 增加變量
static ngx_int_t ngx_http_xxxxxx_add_variable(ngx_conf_t *cf){
ngx_http_variable_t * var;
var = ngx_http_add_variable(cf, &xxxxxx_variable, 0);
if (var == NULL) {
return NGX_OK;
}
//設(shè)置回調(diào)
var->get_handler = ngx_http_variable_xxxxxx_variable; // 獲取該變量時(shí)的回調(diào)方法
var->data = 0;
return NGX_OK;
}
typedef struct {
ngx_str_t xxxxxx_value;
} ngx_http_xxxxxx_ctx_t;
ngx_int_t ngx_http_variable_xxxxxx_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_xxxxxx_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_xxxxxx_module);
if (ctx == NULL) {
v->not_found = 1;
return NGX_OK;
}
v->len = ctx->xxxxxx_value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ctx->xxxxxx_value.data;
return NGX_OK;
}
nginx中的變量分為三種:模塊內(nèi)置變量
跟压、根據(jù)配置動(dòng)態(tài)添加的變量
、內(nèi)置規(guī)則變量
該示例中添加的變量屬于模塊內(nèi)置變量
歼培。
根據(jù)配置動(dòng)態(tài)添加的變量一般當(dāng)解析相應(yīng)指令時(shí)震蒋,在指令的解析函數(shù)中添加。
內(nèi)置規(guī)則變量不需要添加躲庄,而是按特定規(guī)則解析查剖,例如http、upstream_http读跷、arg梗搅、cookie等變量都是nginx根據(jù)請(qǐng)求解析的。
模塊內(nèi)置變量主要是在ngx_http_module_t
的preconfiguration
階段中添加(也即在nginx框架解析配置文件前)效览。
模塊內(nèi)置變量和根據(jù)配置動(dòng)態(tài)添加的變量都是通過(guò)調(diào)用ngx_http_add_variable()
方法添加變量无切。ngx_http_add_variable
方法會(huì)向存儲(chǔ)配置項(xiàng)的結(jié)構(gòu)體ngx_conf_t->variable_key
數(shù)組中添加變量(參數(shù)是ngx_conf_t
和參數(shù)名
等)并返回變量結(jié)構(gòu)。
// nginx中變量結(jié)構(gòu)體的數(shù)據(jù)結(jié)構(gòu)(并非示例代碼)
typedef ngx_variable_value_t ngx_http_variable_value_t;
typedef struct {
unsigned len:28; // 變量值數(shù)據(jù)長(zhǎng)度
unsigned valid:1; // 該變量值是否可用
unsigned no_cacheable:1; // 該變量值是否不能緩存
unsigned not_found:1; // 對(duì)應(yīng)變量不存在
unsigned escape:1; // 變量值內(nèi)容中的特殊字符是否進(jìn)行了轉(zhuǎn)義
u_char *data; // 變量值的數(shù)據(jù)
} ngx_variable_value_t;
nginx所有變量都存儲(chǔ)在ngx_http_core_module
的main
級(jí)別的結(jié)構(gòu)體ngx_http_core_main_conf_t
中丐枉,所以變量的作用范圍是整個(gè)http{}配置哆键。在某個(gè)server中添加的變量,在另一個(gè)server同樣可以使用瘦锹。
本示例每個(gè)http請(qǐng)求處理過(guò)程中都會(huì)更新一下變量籍嘹,所以每個(gè)請(qǐng)求變量值都不一樣
// 設(shè)置某個(gè)階段處理請(qǐng)求的方法
static ngx_int_t ngx_http_xxxxxx_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *w;
ngx_http_core_main_conf_t *cmcf;
cmcf = (ngx_http_core_main_conf_t*)ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
//在NGX_HTTP_CONTENT_PHASE中介入處理代碼,回調(diào)函數(shù)ngx_http_xxxxxx_handler_request可對(duì)http請(qǐng)求做處理
w = (ngx_http_handler_pt*)ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (w == NULL)
{
return NGX_OK;
}
//具體實(shí)現(xiàn)的回調(diào)函數(shù)
*w = ngx_http_xxxxxx_handler_request;
return NGX_OK;
}
nginx相關(guān)配置:
- nginx.conf中在server下增加配置項(xiàng) xxxxxx xxxxxxxxxx
- fastcgi_params中增加變量配置
- php中通過(guò)$_SERVER['變量名']使用弯院,變量名 使用fastcgi_params中配置的變量|