现在的位置: 首页 > 综合 > 正文

Nginx的phase初始化

2014年07月03日 ⁄ 综合 ⁄ 共 6154字 ⁄ 字号 评论关闭

在前面的文章,我们知道当Nginx获取到连接后,会首先对连接的请求进行处理,例如读取请求的uri,参数等,然后会调用ngx_http_core_run_phases函数对于该请求跑一遍phase,当所有的phase都跑完了以后,那么这个请求也就处理完成了。

好,我们首先来看一下Nginx的phase的定义吧:

//Nginx定义的11个phase
typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0,    //读取请求的phase

    NGX_HTTP_SERVER_REWRITE_PHASE,  //这个阶段主要是处理全局的(server block)的rewrite

    NGX_HTTP_FIND_CONFIG_PHASE,   /* 根据uri查找location */  
    
    NGX_HTTP_REWRITE_PHASE,   /* localtion级别的rewrite */  
    
    NGX_HTTP_POST_REWRITE_PHASE,   /* server、location级别的rewrite都是在这个phase进行收尾工作的 */  

    NGX_HTTP_PREACCESS_PHASE,  /* 粗粒度的access */  

    NGX_HTTP_ACCESS_PHASE,   /* 细粒度的access,比如权限验证、存取控制 */  
    
    NGX_HTTP_POST_ACCESS_PHASE,   /* 根据上述两个phase得到access code进行操作 */  

    NGX_HTTP_TRY_FILES_PHASE,    /* 实现try_files指令 */  
    
    NGX_HTTP_CONTENT_PHASE,    /* 生成http响应 */  
   
    NGX_HTTP_LOG_PHASE    /* log模块 */  
} ngx_http_phases;


然后我们在ngx_http_core_main_conf_t结构中可以看到如下的定义:

   /** 
     * 所有的phase的数组,其中每个元素是该phase上注册的handler的数组。 
     */  
    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];


phases静态数组保存了所有的http类型的模块定义的phase处理函数,这里我们看一下ngx_http_phase_t结构:

//phase的定义
typedef struct {
    ngx_array_t                handlers;  //处理函数
} ngx_http_phase_t;


其实说白了每一个phase就对应一个数组,用来保存该phase的所有处理函数。我们首先来看phases数组的是如何初始化的,在http命令的回调函数ngx_http_block中我们可以看到如下代码:

    if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {  //因为nginx把http分化成为11个phase,每一个phase都可以注册handler,ngx_http_core_main_conf_t里面的phases所有的phase handler数组,ngx_http_init_phases函数完成每一个phase的handler的初始化
        return NGX_CONF_ERROR;
    }


调用了ngx_http_init_phases函数来初始化phases数组。该函数其实很简单,因为phases数组中每一个元素都有一个数组用来保存该phase的处理函数,ngx_http_init_phases函数就是调用ngx_array_init来初始化这些数组的。

嗯,接下来我们来看一下在ngx_http_core_main_conf_t中的另外一个比较重要的域:

	/** 
	  * 包含所有phase,以及注册的phase handler,这些handler在处理http请求时, 
	  * 会被依次调用,通过ngx_http_phase_handler_t的next字段串联起来组成一个 
	  * 链表。 
	  */  
    ngx_http_phase_engine_t    phase_engine;


这个域将会包含一个动态数组,用来存储所有的phase的handler,并且将它们组成类似于链表的方式,这样额可以方便以后phase按照顺寻来调用。我们首先来看看ngx_http_phase_engine_t结构的定义吧:

typedef struct {
    ngx_http_phase_handler_t  *handlers;   //它是一个动态数组,用来保存所有的ngx_http_phase_handler_t结构
	   /** 
     * server rewrite阶段的handler在ngx_http_core_main_conf_t->phase_engine.handlers数组中的下标 
     */  
    ngx_uint_t                 server_rewrite_index;

	    /** 
     * rewrite阶段的handler在ngx_http_core_main_conf_t->phase_engine.handlers数组中的下标 
     */  
    ngx_uint_t                 location_rewrite_index;
} ngx_http_phase_engine_t;


handler域是一个动态数组,它将会用来保存所有phase的handler,但是这里保存的元素稍微变了一下,并不是单纯的函数了,而是一个ngx_http_phase_handler_t结构,接下来我们来看看这个结构的定义:

struct ngx_http_phase_handler_s {
    ngx_http_phase_handler_pt  checker;   //当前handler的checker函数,同一phase的所有handlerchecker都是统一的
    ngx_http_handler_pt        handler; //处理函数
    ngx_uint_t                 next;    //下一个phase的第一个handler在phase_engine的handlers数组中的下标,用这种方式将phase链成了一个链表,可以用来按照phase的顺序来调用handler
};


这里checker函数在同一个phase的所有handler里都是一样的,而且最终handler函数的调用也是在checker函数中完成的,handler就是对应的函数,next是下一个phase的第一个handler在phase_engine的handlers数组中下表,nginx就是利用这种方式将phase变成了类似于链表的结构,方便调用。

嗯,接下来我们来看一下phase_engine的初始化,在http命令回调函数中会找到如下代码:

    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {   //初始化phase_engine
        return NGX_CONF_ERROR;
    }


调用了ngx_http_init_phase_handlers函数来初始化phase_engine,好了,接下来我们来看看ngx_http_init_phase_handlers函数吧:

//将ngx_http_core_main_conf_t的phases数组中的handler信息转化为ngx_http_phase_handler_t结构,并将他们保存在phase_engine.handlers数组中
static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    ngx_int_t                   j;
    ngx_uint_t                  i, n;
    ngx_uint_t                  find_config_index, use_rewrite, use_access;
    ngx_http_handler_pt        *h;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_handler_pt   checker;d

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
    find_config_index = 0;
    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;    /* ngx_http_rewrite在http module的postconfiguration回调函数中添加REWRITE阶段的处理函数 */  
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;  /* ngx_http_access在http module的postconfiguration回调函数中添加ACCESS阶段的处理函数 */  

//n用来统计所有的phase 的handler的数量
    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {   //遍历phase数组中的所有的phase,然后将它们handler的数量加起来
        n += cmcf->phases[i].handlers.nelts;
    }

//嗯,这里还加了一个空指针
    ph = ngx_pcalloc(cf->pool,
                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));   //这里说白了是为phase_engine.handlers动态数组分配内存空间,因为最后phase_engine.handlers会指向ph,用来保存所有的ngx_http_phase_handler_t
    if (ph == NULL) {
        return NGX_ERROR;
    }

    cmcf->phase_engine.handlers = ph;  //将phase_engine.handlers指向刚刚的ph
    n = 0;


	/* 
		* 初始化phase handler,保存checker和next字段 
		* continue的都是不能添加handler的phase 
		*/	
    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        h = cmcf->phases[i].handlers.elts;

        switch (i) {

        case NGX_HTTP_SERVER_REWRITE_PHASE:
            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.server_rewrite_index = n;  //记住NGX_HTTP_SERVER_REWRITE_PHASE的第一phase的handler的下标
            }
            checker = ngx_http_core_rewrite_phase;   //NGX_HTTP_SERVER_REWRITE_PHASE的checker

            break;

        case NGX_HTTP_FIND_CONFIG_PHASE:
            find_config_index = n;

            ph->checker = ngx_http_core_find_config_phase;
            n++;
            ph++;

            continue;

        case NGX_HTTP_REWRITE_PHASE:
            if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.location_rewrite_index = n;  //记住NGX_HTTP_REWRITE_PHASE第一个handler的下标
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_POST_REWRITE_PHASE:
            if (use_rewrite) {
                ph->checker = ngx_http_core_post_rewrite_phase;
                ph->next = find_config_index;   //它的next比较怪,指向前面的,这里主要是为了处理uri重写的情况
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_ACCESS_PHASE:
            checker = ngx_http_core_access_phase;
            n++;
            break;

        case NGX_HTTP_POST_ACCESS_PHASE:
            if (use_access) {
                ph->checker = ngx_http_core_post_access_phase;
                ph->next = n;
                ph++;
            }

            continue;

        case NGX_HTTP_TRY_FILES_PHASE:
            if (cmcf->try_files) {
                ph->checker = ngx_http_core_try_files_phase;
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_CONTENT_PHASE:
            checker = ngx_http_core_content_phase;
            break;

        default:
            checker = ngx_http_core_generic_phase;  //其余phase的checker函数是ngx_http_core_generic_phase
        }

        n += cmcf->phases[i].handlers.nelts;  //这里n将表示下一个phase开始的第一个handler的在phase_engine.handlers数组中的下标

        for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) {
            ph->checker = checker;   //为当前ngx_http_phase_handler_t的checker函数赋值,在同一个phase中,checker是统一的,handler不一样
            ph->handler = h[j];   //为handler赋值
            ph->next = n;  // 指向下一个phase的第一个handler,nginx用这种方式将他们串成了一个链
            ph++;
        }
    }

    return NGX_OK;
}


其实该函数还是很简单的,基本上上面的注释都已经说的很清楚了。而且能够很明白nginx是如何将phase的handler组织成为类似于链表的机构方便phase的顺序调用,而且可以知道同一个phase中,其checker函数都是一样的。不过这里有个地方需要注意,就是NGX_HTTP_POST_REWRITE_PHASE部分的下一个phase指向的是NGX_HTTP_FIND_CONFIG_PHASE,这里可能是因为需要一些额外的处理吧。另外可以知道有的phase是只有一个handler的,例如NGX_HTTP_POST_REWRITE_PHASE,有的phase则可以有很多handler。

好了,phase的初始化就讲的差不多了,下一篇文章讲讲是如何调用这些phase来处理http请求的吧。

抱歉!评论已关闭.