nginx中handler的处理(一) 博客分类: 服务器设计 nginxAccess数据结构J#
程序员文章站
2024-03-22 14:00:22
...
nginx中的处理一个http的请求分为了8个phase,分别是下面几个阶段.
其中特别要注意就是几个rewrite阶段。
这里要注意的就是这几个phase的执行是严格按照顺序的,也就是NGX_HTTP_POST_READ_PHASE是第一个,而LOG_PHASE是最后一个。只有一个特殊那就是FIND_CONFIG_PHASE,这个的话,有可能会在后面的rewrite phase再来调用这个phase。
这里handler的结构是这样的,在ngx_http_core_main_conf_t中会有一个包含了ngx_http_phase_t结构的数组,而ngx_http_phase_t包含了一个动态数组,也就是说每一个phase都有一个handler数组。
然后每个handler数组的元素都是一个hanler函数。
也就是我们写handler的时候注册的handler函数。
ok,接下来我们来看phase的初始化,初始化函数是ngx_http_init_phase_handlers。
在看phase初始化之前,我们先来看一个叫做ngx_http_phase_handler_s的结构体,这个结构体是保存在ngx_http_core_main_conf_t 中的,最终我们通过上面所讲的phases注册的handler链会被转换为ngx_http_phase_handler_s,然后保存在ngx_http_core_main_conf_t的phase_engine中。而后面对handler的调用处理都是使用ngx_http_phase_handler_s。
这个结构体是每个handler都会有一个的,也就是说所有的phase handler最终都会链接到一个大的数组中,这个大数组就是ngx_http_phase_engine_t的handlers域。
然后我们来看它的每个域的含义。
checker 所有处于相同phase的handler的check都是相同的,每个phase的handler的调用都是在check中的,也就是check进行一些校验,结果判断等等操作。
handler就是对应的handler处理函数
ngxt 表示了下一个要执行的handler(也就是ngx_http_phase_handler_s)的位置,由于是数组,所以这个也就表示数组索引。而这个默认就是下一个将要执行的phase
来看函数的实现,其实功能很简单,就是初始化ngx_http_phase_handler_s,将我们注册的handler都链接到这个数组中,然后还有一些校验等。
这里要注意有些phase的话只会有一个handler,比如CONFIG_PHASE,下面的代码中我们会详细看到。
这里需要注意就是只有下面这几个phase会有多个handler,剩余的都是只有一个handler的。
接下来我们来看phase的启动。
phase的启动是在ngx_http_core_run_phases这个函数中的,这个函数会遍历所有phase然后调用他们的checker来进行处理,也就是说错误,返回代码的控制什么的都是由各自的checker做的。而所有的checker的返回值都是一样的。
其中特别要注意就是几个rewrite阶段。
typedef enum { //读取请求phase NGX_HTTP_POST_READ_PHASE = 0, //接下来就是开始处理 //这个阶段主要是处理全局的(server block)的rewrite。 NGX_HTTP_SERVER_REWRITE_PHASE, //这个阶段主要是通过uri来查找对应的location。然后将uri和location的数据关联起来 NGX_HTTP_FIND_CONFIG_PHASE, //这个主要处理location的rewrite。 NGX_HTTP_REWRITE_PHASE, //post rewrite,这个主要是进行一些校验以及收尾工作,以便于交给后面的模块。 NGX_HTTP_POST_REWRITE_PHASE, //比如流控这种类型的access就放在这个phase,也就是说它主要是进行一些比较粗粒度的access。 NGX_HTTP_PREACCESS_PHASE, //这个比如存取控制,权限验证就放在这个phase,一般来说处理动作是交给下面的模块做的.这个主要是做一些细粒度的access。 NGX_HTTP_ACCESS_PHASE, //一般来说当上面的access模块得到access_code之后就会由这个模块根据access_code来进行操作 NGX_HTTP_POST_ACCESS_PHASE, //try_file模块,也就是对应配置文件中的try_files指令。 NGX_HTTP_TRY_FILES_PHASE, //内容处理模块,我们一般的handle都是处于这个模块 NGX_HTTP_CONTENT_PHASE, //log模块 NGX_HTTP_LOG_PHASE } ngx_http_phases;
这里要注意的就是这几个phase的执行是严格按照顺序的,也就是NGX_HTTP_POST_READ_PHASE是第一个,而LOG_PHASE是最后一个。只有一个特殊那就是FIND_CONFIG_PHASE,这个的话,有可能会在后面的rewrite phase再来调用这个phase。
这里handler的结构是这样的,在ngx_http_core_main_conf_t中会有一个包含了ngx_http_phase_t结构的数组,而ngx_http_phase_t包含了一个动态数组,也就是说每一个phase都有一个handler数组。
typedef struct { ................................................................... ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]; } ngx_http_core_main_conf_t; typedef struct { //每个phase都会有一个handler数组。 ngx_array_t handlers; } ngx_http_phase_t;
然后每个handler数组的元素都是一个hanler函数。
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
也就是我们写handler的时候注册的handler函数。
ok,接下来我们来看phase的初始化,初始化函数是ngx_http_init_phase_handlers。
在看phase初始化之前,我们先来看一个叫做ngx_http_phase_handler_s的结构体,这个结构体是保存在ngx_http_core_main_conf_t 中的,最终我们通过上面所讲的phases注册的handler链会被转换为ngx_http_phase_handler_s,然后保存在ngx_http_core_main_conf_t的phase_engine中。而后面对handler的调用处理都是使用ngx_http_phase_handler_s。
这个结构体是每个handler都会有一个的,也就是说所有的phase handler最终都会链接到一个大的数组中,这个大数组就是ngx_http_phase_engine_t的handlers域。
typedef struct { //所有的hanler都会在这个数组中. ngx_http_phase_handler_t *handlers; ngx_uint_t server_rewrite_index; ngx_uint_t location_rewrite_index; } ngx_http_phase_engine_t;
然后我们来看它的每个域的含义。
checker 所有处于相同phase的handler的check都是相同的,每个phase的handler的调用都是在check中的,也就是check进行一些校验,结果判断等等操作。
handler就是对应的handler处理函数
ngxt 表示了下一个要执行的handler(也就是ngx_http_phase_handler_s)的位置,由于是数组,所以这个也就表示数组索引。而这个默认就是下一个将要执行的phase
struct ngx_http_phase_handler_s { ngx_http_phase_handler_pt checker; ngx_http_handler_pt handler; ngx_uint_t next; };
来看函数的实现,其实功能很简单,就是初始化ngx_http_phase_handler_s,将我们注册的handler都链接到这个数组中,然后还有一些校验等。
这里要注意有些phase的话只会有一个handler,比如CONFIG_PHASE,下面的代码中我们会详细看到。
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; //最终的handler数组 ngx_http_phase_handler_t *ph; ngx_http_phase_handler_pt checker; cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1; cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1; find_config_index = 0; //是否有使用rewrite以及access。 use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; //开始计算handler 数组的大小 n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */; for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; } //数组分配内存 ph = ngx_pcalloc(cf->pool, n * sizeof(ngx_http_phase_handler_t) + sizeof(void *)); if (ph == NULL) { return NGX_ERROR; } //handler数组放到handlers里面。 cmcf->phase_engine.handlers = ph; //n表示下一个phase的索引。 n = 0; //开始遍历phase handler.这里是一个phase一个phase的遍历。 for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { //取出对应的handler处理函数 h = cmcf->phases[i].handlers.elts; //根据不同的phase来处理 switch (i) { //server重写phase(也就是内部重定向phase) case NGX_HTTP_SERVER_REWRITE_PHASE: //如果有定义重写规则则设置重写handler的索引n. if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.server_rewrite_index = n; } //赋值checker checker = ngx_http_core_generic_phase; break; //config phase只有一个.这里设置 find_config_index,是因为当我们rewrite之后的url就必须重新挂载location的一些结构,因此就需要再次进入这个phase case NGX_HTTP_FIND_CONFIG_PHASE: find_config_index = n; //自己的checker ph->checker = ngx_http_core_find_config_phase; n++; ph++; continue; //rewrite phase case NGX_HTTP_REWRITE_PHASE: if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.location_rewrite_index = n; } //共用的checker checker = ngx_http_core_generic_phase; break; case NGX_HTTP_POST_REWRITE_PHASE: //如果有使用rewrite则给它的checker赋值 if (use_rewrite) { ph->checker = ngx_http_core_post_rewrite_phase; //注意它的next就是find_config phase,也就是说需要重新挂载location的数据。 ph->next = find_config_index; 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; } //这里n刚好就是下一个phase的其实索引 n += cmcf->phases[i].handlers.nelts; //开始遍历当前的phase的handler。 for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) { ph->checker = checker; //每个的handler就是注册的时候的回掉函数 ph->handler = h[j]; //next为下一个phase的索引 ph->next = n; //下一个handler ph++; } } return NGX_OK; }
这里需要注意就是只有下面这几个phase会有多个handler,剩余的都是只有一个handler的。
NGX_HTTP_POST_READ_PHASE NGX_HTTP_SERVER_REWRITE_PHASE, NGX_HTTP_REWRITE_PHASE, NGX_HTTP_PREACCESS_PHASE, NGX_HTTP_ACCESS_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE
接下来我们来看phase的启动。
phase的启动是在ngx_http_core_run_phases这个函数中的,这个函数会遍历所有phase然后调用他们的checker来进行处理,也就是说错误,返回代码的控制什么的都是由各自的checker做的。而所有的checker的返回值都是一样的。
void ngx_http_core_run_phases(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_phase_handler_t *ph; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; while (ph[r->phase_handler].checker) { //调用checker rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); //如果有一个checker返回OK,则后面的phase就不会处理的。 if (rc == NGX_OK) { return; } } }