NGINX模块开发

目标:在nginx层面增加一个模块对请求进行处理(例如:给每个请求生成一个ID,生成ID之后,通过PHP语言可以获取到该ID)

简介:

1、NGINX对一个请求的处理过程

NGINX将http请求划分为11个处理阶段

阶段 说明
NGX_HTTP_POST_READ_PHASE 接收到完整的HTTP头部后处理的阶段
NGX_HTTP_SERVER_REWRITE_PHASE 请求的URI与nginx.conf中的location表达式匹配前,修改请求的URI是一个独立的http阶段(重定向)
NGX_HTTP_FIND_CONFIG_PHASE 根据请求的URI寻找匹配的location表达式,该阶段只能由ngx_http_core_module模块实现,不建议其他http模块重新定义这一阶段的行为
NGX_HTTP_REWRITE_PHASE 根据上一阶段找到的匹配的location表达式修改URI
NGX_HTTP_POST_REWRITE_PHASE 防止由于nginx.conf配置错误导致的重写URL后的死循环,防止死循环的处理方法:一个请求重写URL的次数达到10次就认为死循环,nginx会返回500错误
NGX_HTTP_PREACCESS_PHASE NGX_HTTP_ACCESS_PHASE阶段决定访问权限前,HTTP模块 可以介入的阶段
NGX_HTTP_ACCESS_PHASE 让HTTP模块判断是否允许这个请求访问nginx服务器
NGX_HTTP_POST_ACCESS_PHASE 给上一阶段收尾,如果NGX_HTTP_ACCESS_PHASE阶段的HTTP模块的handler函数返回请求没有权限访问(NGX_HTTP_FORBIDEN或者NGX_HTTP_UNAUTHORIZED),该阶段向用户发送拒绝服务的错误码
NGX_HTTP_TRY_FILES_PHASE 该阶段为了nginx中的try_files配置项而设置,当HTTP访问静态资源时,try_files配置项可以使请求顺序的访问多个静态资源,如果某一次访问失败,继续访问下一静态资源。这个功能是在该阶段中实现的。
NGX_HTTP_CONTENT_PHASE 处理HTTP请求内容的阶段,该阶段是大部分HTTP模块最愿意介入的阶段
NGX_HTTP_LOG_PHASE 处理请求完毕后记录请求日志的阶段。ngx_http_log_module模块在这一阶段加入一个handler方法,每个请求 处理完毕后记录access_log日志

自定义HTTP模块无法介入的阶段:

NGX_HTTP_FIND_CONFIG_PHASE
NGX_HTTP_POST_REWRITE_PHASE
NGX_HTTP_POST_ACCESS_PHASE
NGX_HTTP_TRY_FILES_PHASE

剩余7个模块均可以介入,每个阶段可以介入模块的个数也是没有限制的,多个模块可以介入同一阶段处理同一请求。


下面记录的模块介入的阶段:NGX_HTTP_CONTENT_PHASE(处理HTTP请求内容)

介入NGX_HTTP_CONTENT_PHASE的方法有两种,一种介入方法是处理所有请求,一种介入方法是只处理特定的请求。

下面的介入方法是处理所有请求。
NGINX中关于HTTP阶段的一些处理方法见下一篇(链接)

1、增加编译的配置文件(将自定义HTTP模块编译进nginx)

自定义模块的一般目录结构:

+ ngx_http_xxxxxx_module

  -config

  -ngx_http_xxxxxx_module.c

config文件:编译nginx可执行文件时,会读取该文件获取模块相关路径及模块名称等

ngx_http_xxxxxx_module.c :处理http请求的代码文件

举例:


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:模块名称

// HTTP_MODULES:所有的http处理模块,自定义模块前一定要加上$HTTP_MODULES,该变量表示已经存在的http模块

//  NGX_ADDON_SRCS:模块源文件,$NGX_ADDON_SRCS表示已有的模块源文件路径,$ngx_addon_dir表示使用.configure命令时输入的路径

// 编译命令:

// .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部分

简介:

nginx中的模块分类几类:核心模块,事件模块,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中,该模块会处理的配置项名称
        NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, // 该名称的配置项在conf中允许的配置方式;NGX_HTTP_SRV_CONF表示允许在server块下出现,NGX_CONF_TAKE1表示配置项名称后可以跟一个参数
        ngx_http_xxxxxx_handler_conf,        //解析配置该配置项对应的方法
        NGX_HTTP_SRV_CONF_OFFSET,
        offsetof(ngx_http_xxxxxx_conf_t, xxxxxx),//该配置项对应的参数会被保存到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中有一个模块的数据结构ngx_module_t,所有的nginx模块都要遵循ngx_module_t的定义。nginx启动时会调用模块中定义的方法。ngx_module_t中最重要的是要设置ctxcommands两个成员。ctx用于指向一类 模块的上下文,http类模块中需要指向ngx_http_module_t结构体。commands将处理nginx.conf中的配置项。

NGX_HTTP_MODULE表示该模块为http模块。

NGX_MODULE_V1NGX_MODULE_V1_PADDING两个宏定义模块中未用到的成员的初始值。

上面的定义中ctx指向ngx_http_xxxxxx_module_ctx,commands设置为ngx_http_xxxxxx_commands

ngx_http_xxxxxx_module_ctx:

ngx_http_module_t结构体定义了8个阶段,nginx启动过程中,会调用结构体定义的相关方法。

调用顺序:

阶段 说明
create main configuration 创建存储main级别直属于http块的结构体
create server configuration 创建存储conf中直属于server块的配置的结构体
create location configuration 创建存conf中直属于location块的配置的结构体
preconfiguration 解析配置文件前
init main configuration 初始化main级别配置项
merge server configuration 合并main级别和server级别下同名的配置项
merge location configuration 合并server级别和location级别下的同名配置项
postconfiguration 完成配置项解析

示例代码中

方法ngx_http_xxxxxx_add_variable在解析配置文件前创建一个了一个变量,具体方法实现看后续代码

方法ngx_http_xxxxxx_init则设置了NGX_HTTP_CONTENT_PHASE阶段的处理http请求的方法,具体方法实现看后续代码

ngx_http_xxxxxx_commands:解析nginx.conf

// ngx系统方法解析配置项
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);    //调用ngx_conf_set_str_slot 处理ngx_str_t类型的变量
    return NGX_CONF_OK;
}

ngx_http_xxxxxx_handler_conf方法解析nginx.conf配置,会将config中配置的值解析到结构体ngx_http_xxxxxx_conf_t中。该方法在nginx启动过程中被调用。

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;
    }

    //设置回调
    var->get_handler = ngx_http_variable_xxxxxx_variable; // 获取该变量时的回调方法
    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中的变量分为三种:模块内置变量根据配置动态添加的变量内置规则变量

该示例中添加的变量属于模块内置变量

根据配置动态添加的变量一般当解析相应指令时,在指令的解析函数中添加。

内置规则变量不需要添加,而是按特定规则解析,例如http、upstream_http、arg、cookie等变量都是nginx根据请求解析的。

模块内置变量主要是在ngx_http_module_tpreconfiguration阶段中添加(也即在nginx框架解析配置文件前)。

模块内置变量和根据配置动态添加的变量都是通过调用ngx_http_add_variable()方法添加变量。ngx_http_add_variable方法会向存储配置项的结构体ngx_conf_t->variable_key数组中添加变量(参数是ngx_conf_t参数名等)并返回变量结构。

// nginx中变量结构体的数据结构(并非示例代码)
typedef ngx_variable_value_t  ngx_http_variable_value_t;

typedef struct {

unsigned    len:28; //  变量值数据长度
unsigned    valid:1;  // 该变量值是否可用
unsigned    no_cacheable:1; // 该变量值是否不能缓存
unsigned    not_found:1;  // 对应变量不存在
unsigned    escape:1;  // 变量值内容中的特殊字符是否进行了转义

u_char      *data;  // 变量值的数据
}  ngx_variable_value_t;

nginx所有变量都存储在ngx_http_core_modulemain级别的结构体ngx_http_core_main_conf_t中,所以变量的作用范围是整个http{}配置。在某个server中添加的变量,在另一个server同样可以使用。

本示例每个http请求处理过程中都会更新一下变量,所以每个请求变量值都不一样

// 设置某个阶段处理请求的方法
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中介入处理代码,回调函数ngx_http_xxxxxx_handler_request可对http请求做处理
    w = (ngx_http_handler_pt*)ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);

    if (w == NULL)
    {
        return NGX_OK;
    }

    //具体实现的回调函数
    *w = ngx_http_xxxxxx_handler_request;

    return NGX_OK;

}

nginx相关配置:

  • nginx.conf中在server下增加配置项 xxxxxx xxxxxxxxxx
  • fastcgi_params中增加变量配置
  • php中通过$_SERVER['变量名']使用,变量名 使用fastcgi_params中配置的变量|
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容