美文网首页
NGINX模块开发

NGINX模块开发

作者: 晾那呗 | 来源:发表于2018-07-31 13:31 被阅读0次

    目标:在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中配置的变量|

    相关文章

      网友评论

          本文标题:NGINX模块开发

          本文链接:https://www.haomeiwen.com/subject/rezcvftx.html