• Nginx:handler模块按处理阶段挂载原理


    参考资料<深入理解Nginx>(陶辉)

    在Nginx中,handler模块真正的处理函数通过两种方式挂载到处理过程中,一种方式就是按处理阶段挂载;另外一种挂载方式就是按需挂载。

    本次我们将讨论按处理阶段挂载的方法和原理。

    Nginx把HTTP请求的处理过程分为11个阶段:

    typedef enum {
        NGX_HTTP_POST_READ_PHASE = 0,   // 接收到完整的HTTP头部后处理的阶段
     
        NGX_HTTP_SERVER_REWRITE_PHASE,  // URI与location匹配前,修改URI的阶段,用于重定向
     
        NGX_HTTP_FIND_CONFIG_PHASE,     // 根据URI寻找匹配的location块配置项
        NGX_HTTP_REWRITE_PHASE,         // 上一阶段找到location块后再修改URI
        NGX_HTTP_POST_REWRITE_PHASE,    // 防止重写URL后导致的死循环
     
        NGX_HTTP_PREACCESS_PHASE,       // 下一阶段之前的准备
     
        NGX_HTTP_ACCESS_PHASE,          // 让HTTP模块判断是否允许这个请求进入Nginx服务器
        NGX_HTTP_POST_ACCESS_PHASE,     // 向用户发送拒绝服务的错误码,用来响应上一阶段的拒绝
     
        NGX_HTTP_TRY_FILES_PHASE,       // 为访问静态文件资源而设置
        NGX_HTTP_CONTENT_PHASE,         // 处理HTTP请求内容的阶段,大部分HTTP模块介入这个阶段
     
        NGX_HTTP_LOG_PHASE              // 处理完请求后的日志记录阶段
    } ngx_http_phases;

    如果希望某个ngx_http_handler_pt处理方法应用于所有的用户请求,应该使用按处理阶段挂载。

    挂载的时候应该实现该HTTP模块的postconfiguration方法,挂载代码如下:

    static ngx_int_t ngx_http_mytest_init(ngx_conf_t *cf)
    {
        //获取到全局的ngx_http_core_main_conf_t结构体
        ngx_http_core_main_conf_t *cmcf=ngx_http_conf_get_module_main_conf(cf,ngx_http_core_module);
        /*
    phases数组有11个成员,代表上面所说的11个阶段,取出需要挂载的某个阶段(NGX_HTTP_POST_READ_PHASE)的handlers动态数组,
    这样该模块就介入HTTP请求的(NGX_HTTP_POST_READ_PHASE)处理阶段了
    */ h=ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers); if(h==NULL){ return NGX_ERROR; } *h=ngx_http_mytest_handler; }

    下面分析一下该方法涉及到的数据结构,首先是全局的ngx_http_core_main_conf结构体:

    typedef struct {
        //该结构包含一个handler处理方法的数组,是HTTP请求处理顺序的关键
        ngx_http_phase_engine_t phase_engine;
        //用于在HTTP框架初始化时添加HTTP处理方法
        ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE+1];
        ...
    } ngx_http_core_main_conf_t;

    其中phases是一个数组,该数组元素的定义如下

    typedef struct {
        //该动态数组保存着每一个HTTP模块初始化时添加到当前阶段的处理方法
    ngx_array_t handlers; } ngx_http_phase_t;

    因此,操作phases中的handlers成员,HTTP框架就会自动的向相应的处理阶段添加处理方法。

    事实上,HTTP框架将使用phases数组来构建phase_engine成员。ngx_http_phase_engine_t结构如下:

    typedef struct {
        //handlers是一个数组的首地址,它表示一个请求可能经历的所有处理方法(可以说,每个方法对应一个序号,保证了HTTP处理的顺序执行)
        ngx_http_phase_handler_t *handlers;
        ...
    } ngx_http_phase_engine_t;

    其中ngx_http_phase_handler_t定义如下:

    typedef struct ngx_http_parse_handler_s ngx_http_parse_handler_t;
    typedef ngx_int_t (*ngx_http_pharse_handler_pt) (ngx_http_request_t *r,ngx_http_phase_handler_t *ph);
    typedef ngx_int_t (*ngx_http_handler_pt) (ngx_http_request_t *r);
    //该结构体只代表处理阶段中的一个处理方法 struct ngx_http_phase_handler_s { //在处理某一个HTTP阶段时,HTTP框架将调用checker方法来处理请求,只有checker方法才会调用handler方法(checker方法都是由框架中ngx_http_core_module模块实现的) ngx_http_phase_handler_pt checker; //第三方模块通过定义handler方法才能介入一个HTTP处理阶段以处理请求(HTTP框架会帮我们设置该成员,在此之前我们要设置好一些初始化信息:如上面的parses.handlers动态数组) ngx_http_handler_pt handler; //将要执行的下一个HTTP处理阶段的序号,通常表示下一个处理阶段的第一个ngx_http_phase_handler_t处理方法 ngx_uint_t next; }

    下面将用一个例子来说明

    当HTTP框架在建立的TCP连接上接收到客户发送的完整HTTP请求头部时,开始执行NGX_HTTP_POST_READ_PHASE阶段的checker方法

    关键是判断handler的返回值来判断执行的顺序(前面ngx_http_phase_engine_t包含了全部的处理方法,每个方法对应一个序号,根据序号就可以判断下一步执行的是哪个处理方法)

    ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,ngx_http_phase_handler_t *ph)
    {
        //调用这一阶段中个HTTP模块添加的handler处理方法
        ngx_int_t rc=ph->handler(r);
        //如果hanlder方法返回NGX_OK,将进入下一阶段处理
        if(rc==NGX_OK){
            r->phase_handler=ph->next;
            return NGX_AGAIN;
        }
        //如果handler方法返回NGX_DECLINED,那么将进入下一个处理方法,这个处理方法即可以属于当前阶段,也可能属于下一阶段
        if(rc==NGX_DECLINED){
            r->phase_handler++;
            return NGX_AGAIN;
        }
        //如果handler方法返回NGX_AGAIN或者NGX_DONE,那么当前请求将依然停留在这一处理阶段
        if(rc==NGX_AGAIN||rc==NGX_DONE){
            return NGX_OK;
        }
        //如果hanlder方法返回其他,则调用ngx_http_finalize_request结束请求
        ngx_http_finalize_request(r,rc);
        return NGX_OK;
    }

    在此阶段中ngx_http_handler_pt方法的返回值可以产生4中不同的影响

  • 相关阅读:
    Linux下基于PAM机制的USB Key的制作
    IP-route管理路由
    Linux下CPU主板监控工具lm_sensors
    两个网卡隔离方法
    关机后内存的数据是怎么丢失的呢?
    6.0 移动端页面布局
    CyberPlayer 使用教程
    5.10 HTML5 音频和视频
    让Ecshop网店系统用户自动登陆
    設定 Bootstrap/SASS/Bower/gulp (Windows平台)
  • 原文地址:https://www.cnblogs.com/runnyu/p/4910952.html
Copyright © 2020-2023  润新知