您的位置:首页 > 理论基础 > 计算机网络

Nginx基础. HTTP多阶段处理大致分析

2015-10-15 19:32 771 查看
HTTP处理阶段的规则:

对于每个HTTP阶段, 它都包括checker检查方法和handler处理方法

typedef struct ngx_http_phase_handler_s ngx_http_phase_handler_t;

//一个HTTP处理阶段中的checker方法, 仅可以由HTTP框架实现, 以此控制HTTP请求的处理流程.
typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);

//由HTTP模块实现的handler处理方法
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);

//要注意的是, ngx_http_phase_handler_t结构体仅表示处理阶段中的一个处理方法
struct ngx_http_phase_handler_s{
//在处理到某个HTTP处理阶段时, HTTP框架将会在checker方法已实现的前提下首先调用checker方法来处理请求. 而不会
//直接调用任何阶段中的handler方法, 只有在checker方法中才会去调用handler方法
//因此, 事实上所有的checker方法都是由框架中的ngx_http_core_module模块实现的
//且普通的http模块无法重定义checker方法
ngx_http_phase_handler_pt checker;
//除ngx_http_core_Module模块之外的HTTP模块, 只能通过定义handler方法才能介入某个HTTP处理阶段以处理请求.
ngx_http_handler_pt handler;
//将要执行的下一个HTTP处理阶段的序号
//使用来next就使得处理阶段不必按顺序依次执行了, 既可以向后跳跃多个阶段执行, 也可以跳到之前执行过的阶段重新执行
//通常, next表示下一个处理阶段中第一个ngx_http_phase_handler_t处理方法
ngx_uint_t next;
};


在任意一个ngx_http_phase阶段, 都可以拥有零个或多个ngx_http_phase_handler_t结构体.

一个http{}块解析完后, 就会根据nginx.conf中的配置产生由ngx_http_phase_handler_t组成的数组, 在处理HTTP请求时, 一般情况下这些阶段是顺序向后执行的, 但ngx_http_phase_handler_t结构体中的next成员使得他们可以非顺序执行. ngx_http_phase_engine_t结构体就是所有ngx_http_phase_handler_t组成的数组.

typedef struct {
//这里的handlers表示由ngx_http_phase_handler_t组成的数组, 它表示一个请求可能经历的所有ngx_http_handler_pt处理方法
ngx_http_phase_handler_t *handlers;
//表示NGX_HTTP_SERVER_REWRITE_PHASE阶段第一个ngx_http_phase_handler_t处理方法在handlers中的序号, 用于在执行HTTP请求的任何阶段中快速跳转到NGX_HTTP_SERVER_REWRITE_PHASE阶段处理请求
ngx_uint_t server_rewrite_index;
//表示NGX_HTTP_REWRITE_PHASE阶段第一个ngx_http_phase_handler_t处理方法在handlers中的序号, 用于在执行HTTP请求的任何阶段中快速跳转到NGX_HTTP_REWRITE_PHASE阶段处理请求
ngx_uint_t location_rewrite_index;
}ngx_http_phase_engine_t;
可见, 此结构体中保存来在当前nginx.conf下一个用户请求可能经历的所有ngx_http_handler_pt处理方法, 这个结构体是保存在全局的ngx_http_core_main_conf_t结构体中的

typedef struct  {
ngx_http_phase_engine_t phase_engine;
ngx_http_phase_t phase[NGX_HTTP_LOG_PHASE+1];
...
}ngx_http_core_main_conf_t;
这里的phase_engine成员控制运行过程中一个HTTP请求所要经过的HTTP处理阶段, 它将配合ngx_http_request_t结构体中的phase_handler成员使用(该成员指定了当前请求应当执行哪一个HTTP阶段)

在HTTP框架初始化的过程中, 任何HTTP模块都可以在ngx_http_module_t中的postconfiguration方法中将自定义的方法添加到handler动态数组中, 这样这个方法就会被最终添加到phase_engine中.

各阶段checker方法分析

下面先看一下各个阶段. 在11个阶段中有7个阶段是允许各个HTTP模块向阶段中任意添加自己实现的handler方法的.同一个阶段的所有handler方法都拥有相同的checker方法. 且每个阶段中的处理方法的返回值都会以不同的方式影响HTTP框架的行为. 下面是HTTP框架在首次从业务上处理请求方法中调用的ngx_http_core_run_phases方法.

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) {

rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

if (rc == NGX_OK) {
return;
}
}
}
(此方法在上面对各个结构体的介绍过后显得十分容易理解. 就是循环调用每个阶段的checker方法, 因此没作注释)

从该方法中可以看出, 方法只会调用每个处理阶段的checker方法, 而不会亲自执行每个阶段中的handler方法. 这是因为handler方法仅在checker方法中被执行, checker方法则由HTTP框架提供, 这样就可以控制各个HTTP模块实现的处理方法在不同的阶段采用不同的调用行为.

还可以看出的是, 如果checker方法返回NGX_OK, 那么整个循环就会结束. 这是因为在接收完HTTP头部后, 是很难在一次NGINX框架的调度中处理完成一个请求的. 在第一次接收完HTTP头部后, HTTP框架就调度ngx_http_process_request方法来对请求进行首次业务处理. ngx_http_core_run_phases方法中就是从中摘取出的. 当某个阶段的某个HTTP模块无法在这一次框架调度中完成任务时(对应的checker方法中的ngx_http_handler_pt可能返回NGX_AGAIN,
即表示需要读取更多的数据才能完成), 就不再继续调用HTTP模块的handler进行处理. 继而把控制权交还给NGINX框架, 当这个请求上对应的事件再次触发时, HTTP框架将执行ngx_http_request_handler方法开始继续处理请求而不是首次调用的ngx_http_process_request方法. 在ngx_http_request_handler方法中, 会从ngx_http_process_request方法断开的地方继续处理请求. 这里只是简单的描述, 更详细的以后会有.

下面就针对每个阶段进行了解.

1. NGX_HTTP_POST_READ_PHASE阶段.(可添加HTTP模块)

在接收到完整的HTTP头部后处理的阶段. 任意想要在NGX_HTTP_POST_READ_PHASE阶段处理请求的HTTP模块, 必须首先在ngx_http_core_main_conf_t中的phase[NGX_HTTP_POST_READ_PHASE]动态数组中添加自己实现的ngx_http_handler_pt方法.

在此阶段, 若ngx_http_handler_pt方法的返回值为:

1). NGX_OK: 执行下一个ngx_http_phase阶段的第一个ngx_http_handler_pt方法. 也就是说, 如果本模块返回了NGX_OK, 那么即使当前阶段还有模块方法没有执行,也不会再执行. 如果下一个阶段没有模块设置模块处理方法, 那么会继续寻找之后的阶段.

2). NGX_DECLINED: 按照顺序执行下一个ngx_http_handler_pt方法. 顺序就是在phase_engine中所有ngx_http_handler_pt组成数组的顺序(所以下一个handler有可能属于本阶段, 也可以不属于本阶段)

3). NGX_AGAIN / NGX_DONE: 当前的ngx_http_handler_pt处理尚未结束. 此时会将控制权交还给事件模块, 下次可写事件再次发生时再次执行该ngx_http_handler_pt方法.

checker方法为ngx_http_core_generic_phase.

ngx_int_t
ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
ngx_int_t  rc;
//这里直接调用此模块的第一个handler, 根据返回值作出不同的处理
//当然, 这个handler调用是不能阻塞的. 直接返回.
rc = ph->handler(r);
//从ngx_http_core_run_phases方法可以看出, 如果checker方法返回的不是NGX_OK, 那么会继续执行HTTP模块处理函数
//只是执行下一个HTTP模块处理函数还是下一个阶段的第一个HTTP模块处理函数, 就要看当前的HTTP模块处理函数的返回值了
if (rc == NGX_OK) {
r->phase_handler = ph->next;
return NGX_AGAIN;
}

if (rc == NGX_DECLINED) {
r->phase_handler++;
return NGX_AGAIN;
}

if (rc == NGX_AGAIN || rc == NGX_DONE) {
return NGX_OK;
}

/* rc == NGX_ERROR || rc == NGX_HTTP_...  */
//出错了, 结束请求
ngx_http_finalize_request(r, rc);

return NGX_OK;
}


2. NGX_HTTP_SERVER_REWRITE_PHASE阶段(可添加HTTP模块)

对于这个阶段, 不好做什么分析.

在此阶段, 若ngx_http_handler_pt方法的返回值为:

1). NGX_DONE: 当前的ngx_http_handler_pt方法尚未结束, 该处理方法需要被再次调用

2). NGX_DECLINED: 当前ngx_http_handler_pt方法处理完毕. 按照顺序执行下一个ngx_http_handler_pt方法

3). NGX_AGAIN, NGX_ERROR: 需要结束请求

checker方法为ngx_http_rewrite_handler方法.

在ngx_http_rewrite_handler方法中, 未曾出现过可能会导致跨过同一HTTP阶段的其他处理方法的代码. 这是因为有许多模块会在NGX_HTTP_SERVER_REWRITE_PHASE阶段同时处理重写URL这样的业务.HTTP认为像这样的阶段(包括另一个阶段NGX_HTTP_REWRITE_PHASE), 每个HTTP模块都应该是完全平等的. 序号靠前的HTTP模块优先级并不会更高, 它不能决定序号靠后的HTTP模块是否可以再次重写URL

3.NGX_HTTP_FIND_CONFIG_PHASE阶段(不可添加HTTP模块)

这是一个关键阶段, 这个阶段是不可已跳过的. 它必须要存在, 这是HTTP基于location设计的基础.

不过, 这个阶段的ngx_http_rewrite_handler方法已经被HTTP框架提供了. 所以不能再向这个阶段添加模块.

ngx_http_rewrite_handler方法实际上就是根据NGX_HTTP_SERVER_REWRITE_PHASE阶段重写的URI检索出匹配的location块的. 其原理为从location组成的静态二叉树中快速检索.

4. NGX_HTTP_REWRITE_PHASE阶段(可添加HTTP模块)

NGX_HTTP_FIND_CONFIG_PHASE阶段检索到location后有机会再次利用rewrite URL,这一工作就是在NGX_HTTP_REWRITE_PHASE阶段完成的.

checker方法与NGX_HTTP_SERVER_REWRITE_PHASE阶段

5. NGX_HTTP_POST_REWRITE_PHASE阶段(不可添加HTTP模块)

这个阶段就像是NGX_HTTP_FIND_CONFIG_PHASE阶段一样, 只能由HTTP框架实现, 不允许HTTP模块向该阶段添加处理方法

此阶段的checker方法是 ngx_http_core_post_rewrite_phase, 它的目的在于检查rewrite重写URL的次数不可以超过10次, 以此防止rewrite死循环而造成整个Nginx服务器都不可用

6. NGX_HTTP_PREACCESS_PHASE阶段(可添加HTTP模块)

此阶段一般用于对当前请求进行限制性处理.

checker方法与第一个阶段NGX_HTTP_POST_READ_PHASE阶段调用的checker方法一样.

7. NGX_HTTP_ACCESS_PHASE阶段(可添加HTTP模块)

此阶段与NGX_HTTP_PREACCESS_PHASE阶段很大的不同在于, 它的checker方法是ngx_http_core_access_phase方法.

此阶段实际上与nginx.conf配置文件中的staisfy配置项有紧密的联系, 所以任何介入NGX_HTTP_ACCESS_PHASE阶段的HTTP模块, 在实现ngx_http_handler_pt方法时都需要注意staisfy的参数.

这一阶段用于控制用户发起的请求是否合法. 比如检测客户端的IP地址是否允许访问. satisfy参数尤其重要.

在此阶段, 若ngx_http_handler_pt方法的返回值为:

1). NGX_OK: 如果在配置文件中配置了satisfy all, 那么将按照顺序执行下一个ngx_http_handler_pt方法; 如果配置了 satisfy any, 那么将会执行下一个 ngx_http_phase阶段的第一个ngx_http_handler_pt方法

2). NGX_DECLINED: 按照顺序执行下一个ngx_http_handler_pt方法

3). NGX_AGAIN, NGX_DONE: 当前的ngx_http_handler_pt方法尚未结束, 当前方法有机会被再次调用.

4). NGX_HTTP_FORBIDDEN: 如果在配置文件中配置了 satisfy any, 那么将ngx_http_request_t中的access_code成员设置为返回值. 按照顺序执行下一个ngx_http_handler_pt处理方法; 如果是satisfy all, 那么调用ngx_http_finalize_request结束请求.

5). NGX_ERROR: 调用ngx_http_finalize_request结束请求.

checker方法是ngx_http_core_access_phase方法.

ngx_int_t
ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
ngx_int_t                  rc;
ngx_http_core_loc_conf_t  *clcf;

if (r != r->main) {
//如果是子请求, 那么就不需要进行access判断了. 因为既然原请求都允许了, 其子请求就更不用说了.
r->phase_handler = ph->next;
return NGX_AGAIN;
}
//立即执行这个阶段的第一个handler. 根据上面写出来的不同返回值及处理方式进行对照.
rc = ph->handler(r);

if (rc == NGX_DECLINED) {    //对于这里, 出现了些许疑惑, 为什么可以直接执行下一个handler, 这个返回值是access判断成功了还是失败了呢? 那么我推测, 这应该是这个阶段中不进行access判断的模块, 而是进行一些其他处理.
r->phase_handler++;
return NGX_AGAIN;
}

if (rc == NGX_AGAIN || rc == NGX_DONE) {
return NGX_OK;
}

//不是上面两种结果, 那么就需要根据satisfy的参数来进行不同的操作了. 返回结果可能是OK或是FORBIDDEN,当然还有其他.
//因为satisfy参数存储在ngx_http_core_loc_conf_t中, 索取取出这个结构体
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
//如果返回的是OK, 那么表明此模块access测试正常, 又因为是ALL, 所以继续下一个模块测试
if (rc == NGX_OK) {
r->phase_handler++;
return NGX_AGAIN;
}

} else {
//如果返回的是OK, 那么表明此模块access测试正常, 又因为是ANY, 那么不再执行其他判断, 直接跳到下一个phase的首个handler
if (rc == NGX_OK) {
r->access_code = 0;

if (r->headers_out.www_authenticate) {
r->headers_out.www_authenticate->hash = 0;
}

r->phase_handler = ph->next;
return NGX_AGAIN;
}

//但如果返回的是FORBIDDEN或是UNAUTHORIZED, 因为是ANY, 所以可以到下一个HTTP模块继续判断.
if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {
if (r->access_code != NGX_HTTP_UNAUTHORIZED) {
r->access_code = rc;
}

r->phase_handler++;
return NGX_AGAIN;
}
}

/* rc == NGX_ERROR || rc == NGX_HTTP_...  */
//出错了, 直接结束请求.
//除了出错, 这里还包含一种情况, 就是在satisfy为ALL的情况下, 本模块执行的返回结果不是OK
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
虽然ngx_http_core_access_phase方法较为复杂, 但当我们开发的HTTP模块需要处理请求的访问权限时, 就会发现此方法带给我们的强大功能. 可以实现复杂的权限控制

看到这里, 我有了些疑惑, 如果要求: (a && b) || c 的情况, 该怎么处理呢?

8. NGX_HTTP_POST_ACCESS_PHASE阶段.(不可添加HTTP模块)

这个阶段只能由HTTP框架实现.

这个阶段完全是为了之前的NGX_HTTP_ACCESS_PHASE阶段服务的. 如果没有任何HTTP模块介入NGX_HTTP_ACCESS_PHASE阶段处理请求, 那么此阶段也就不会存在.

此阶段的checker方法是ngx_http_core_post_access_phase. 主要用于检查ngx_http_request_t请求中的access_code成员.当其不为0时就结束请求了. 否则继续执行下一个模块处理方法.

9. NGX_HTTP_TRY_FILES_PHASE阶段(不可添加HTTP模块)

这个阶段只能由HTTP框架实现.

此阶段的checker方法是 ngx_http_core_try_files_phase, 它与nginx.conf中的try_files配置项密切相关的. 如果try_files后指定的静态文件资源中有一个可以访问, 那么这时候就会直接读取文件并发送响应给客户, 不会再向下执行后续的阶段. 如果所有的静态文件资源都无法执行, 就继续执行下一个模块处理方法.

10. NGX_HTTP_CONTENT_PHASE阶段(可添加HTTP模块)

在之前这篇模块开发介绍的文章(http://blog.csdn.net/u012062760/article/details/47973897)最后, 我们提到模块的处理函数有两种方式可以添加到HTTP框架中.

一种是利用ngx_http_module_t中的接口postconfiguration; 另一种是直接给ngx_http_core_loc_conf_t结构体中的handler成员赋值.

第一种方法适用于任何一个可以添加模块的阶段, 而第二种方法则只能适用于此阶段.

再次回顾此片文章中的模块处理函数挂载方式:

static char *ngx_http_ben_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){
ngx_http_core_loc_conf_t *clcf;

//首先找到ben_test配置项所属的模块
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
//将其处理函数设置为ngx_http_ben_test_handler
clcf->handler = ngx_http_ben_test_handler;

//处理参数, 由nginx内部提供, 用于将配置项后的参数存储到此模块的存储结构中去
ngx_conf_set_str_slot(cf, cmd, conf);

return NGX_CONF_OK;
}
若是当初, 肯定是没有看懂. 现在来看的话, 就明朗许多, 目的是给ngx_http_core_loc_conf_t结构体的handler赋值.所以这里使用的是第二种挂载方法.

且要注意的是, 此函数是在遇到某个配置项后的回调函数.

下面再贴出使用第一种挂载方法的例子:

static ngx_int_t
ngx_http_hello_init(ngx_conf_t *cf)
{
ngx_http_handler_pt        *h;
ngx_http_core_main_conf_t  *cmcf;

cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}

*h = ngx_http_hello_handler;

return NGX_OK;
}
这里也很明朗, 目的就是往存放在ngx_http_core_main_conf_t中的数组成员phases中的某个动态数组中添加一个处理函数.

且要注意的是, 此函数实现的是ngx_http_module_t中的postconfiguration接口.

这两种添加方式的不同, 也使得他们的作用产生了巨大的差异.

对于第二种挂载方法, 它不再应用于所有的HTTP请求, 仅仅当用户请求的URI匹配了location时(上面说道, 该函数是配置项的回调函数, 所以只有当匹配的location中出现了该配置项, 才会被调用)才会被调用. 这就是最大的差异.

因此, 当HTTP模块实现了某个ngx_http_handler_pt处理方法并希望介入NGX_HTTP_CONTENT_PHASE阶段来处理用户请求时, 如果希望这个处理方法应用于所有的用户请求, 则应该在ngx_http_module_t中的postconfiguration接口实现, 向ngx_http_core_main_conf_t中的phase动态数组中添加这个处理方法; 反之, 如果希望这个方式仅应用于URI匹配的某些location的用户请求, 就应该在配置项的回调方法中实现, 把ngx_http_handler_pt方法设置到ngx_http_core_loc_conf_t中去.

根据上面一段, 我们也可以看出, 如果使用第一种挂载方法, 那么就算某location中没有该模块配置项, 也会调用该模块的处理方法.因为它的添加方式是针对所有连接的. 然而第二种方法则要求一定要出现该配置项, 否则该方法不会被调用. 又因为ngx_http_core_loc_conf_t中仅有一个handler指针, 它不是数组, 所以如果使用这第二种方法挂载的话, 每个请求在NGX_HTTP_CONTENT_PHASE阶段只能有一个处理方法. 再继续深入, 既然只有一个指针, 那么如果此配置项以第二种方式挂载,
此时又有另一个配置项以此种方式挂载, 且出现在同一个location中, 那么这就会导致之前的函数指针被后来的覆盖.

此阶段的checker方法是ngx_http_core_content_phase.

ngx_int_t
ngx_http_core_content_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
size_t     root;
ngx_int_t  rc;
ngx_str_t  path;

//这里的content handler即为ngx_http_core_loc_conf_t中的handler
//之所以在ngx_http_request_t结构体中也对应这个函数指针, 是为了加快处理速度. 在NGX_HTTP_FIND_CONFIG_PHASE阶段就会把这个成员设置为匹配了请求URI的location块中对应的ngx_http_core_loc_conf_t结构体中的handler成员
//如果该指针是存在的, 那么说明该location中有配置项以第二种方式挂载, 那么只会执行它一个处理函数了
if (r->content_handler) {
//设置什么都不做的ngx_http_request_empty_handler方法, 再有可写事件时就直接把控制权交还给事件模块
//因为HTTP框架在这一阶段调用HTTP模块处理请求就意味着接下来只希望该模块处理请求, 设置为ngx_http_request_empty_handler方法可以防止HTTP模块异步的处理请求时却又其他HTTP模块还在同时处理可写事件, 向客户端发送响应
r->write_event_handler = ngx_http_request_empty_handler;
//可以看到, 直接调用了ngx_http_finalize_request函数结束处理连接
ngx_http_finalize_request(r, r->content_handler(r));
return NGX_OK;
}

//否则的话, 就采取与其他阶段一样的处理方法, 将此阶段的处理方法都执行(当然,情况依赖返回值)
rc = ph->handler(r);
//如果返回的不是NGX_DECLINED, 那么结束处理连接
if (rc != NGX_DECLINED) {
ngx_http_finalize_request(r, rc);
return NGX_OK;
}

/* rc == NGX_DECLINED */
//否则, 当返回的是NGX_DECLINED时, 继续执行下一个模块处理函数
ph++;
//检测当前的handler方法是否已经是最后一个handler了呢?
//如果下一个结构体中的checker方法不存在了, 那么就表明确实是最后一个; 反之, 说明还有handler需要执行
if (ph->checker) {
r->phase_handler++;
return NGX_AGAIN;
}

//走到这里, 表明已经没有handler需要执行了. 此时就需要结束请求了
//但需要根据URI确定返回什么样的HTTP响应.
//如果URI是以'/'结尾, 则以NGX_HTTP_FORBIDDEN作为参数调用ngx_http_finalize_request结束
if (r->uri.data[r->uri.len - 1] == '/') {

if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"directory index of \"%s\" is forbidden", path.data);
}

ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
return NGX_OK;
}
//如果不是以'/'结尾, 那么以NGX_HTTP_NOT_FOUND作为参数调用ngx_http_finalize_request结束
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");

ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_OK;
}
简单的总结一下, 但我们使用第一种方式挂载处理方法, 如果该方法返回NGX_DECLINED, 将按顺序向后执行下一个ngx_http_handler_pt方法;

如果使用第二种方法挂载, 那么无论ngx_http_handler_pt返回什么都会直接调用ngx_http_finalize_request方法结束请求

11. NGX_HTTP_LOG_PHASE阶段

这是用来做日志记录的阶段. 如果洗完挂载请求的最后阶段做一些共性的收尾工作, 那么不妨将ngx_http_handler_pt处理方法添加到这个阶段

但是, 值得注意的是, 在NGX_HTTP_CONTENT_PHASE阶段结束部分并没有去调用这个阶段的处理方法.

事实上, 记录访问日志是必须在请求将要结束时才能进行的. 因此, 这个阶段的回调方法在最后的ngx_http_free_request才会调用.

HTTP框架的初始化

最后, 分析一下HTTP框架的初始化流程. 其实就是在遇到http{}后执行的内容:(部分省略)

因为这里面部分已经在上一篇文章介绍过, 所以只是粗略提一下.

static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char                        *rv;
ngx_uint_t                   mi, m, s;
ngx_conf_t                   pcf;
ngx_http_module_t           *module;
ngx_http_conf_ctx_t         *ctx;
ngx_http_core_loc_conf_t    *clcf;
ngx_http_core_srv_conf_t   **cscfp;
ngx_http_core_main_conf_t   *cmcf;

//这里就是上一篇文章中讲的ngx_http_conf_ctx_t类型结构体, 存储不同级别的不同配置项所需结构体
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
*(ngx_http_conf_ctx_t **) conf = ctx;
//为每个HTTP模块指定在HTTP模块中的顺序
ngx_http_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

ngx_modules[m]->ctx_index = ngx_http_max_module++;
}
//初始化存储不同级别的结构体的3个数组
ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_http_max_module);
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

//调用create_main_conf, create_srv_conf, create_loc_conf创建每个HTTP模块所需的结构体
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
if (module->create_main_conf) {...}
if (module->create_srv_conf) {...}
if (module->create_loc_conf) {...}
}

//在解析http{}内的配置项之前, 调用ngx_http_module_t中的preconfiguration函数, 如果存在的话
pcf = *cf;
cf->ctx = ctx;

for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = ngx_modules[m]->ctx;
if (module->preconfiguration) {
}
}
}

/* parse inside the http{} block */
//开始解析http{}模块, 在解析的过程中, 如果遇到server{}, 那么就会调用ngx_http_core_server方法
//如果遇到location{}, 那么就会调用ngx_http_core_location方法, 这里不再深入
cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
rv = ngx_conf_parse(cf, NULL);

if (rv != NGX_CONF_OK) {
goto failed;
}
//合并同名配置项
cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
cscfp = cmcf->servers.elts;

for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;

if (module->init_main_conf) {}
rv = ngx_http_merge_servers(cf, cmcf, module, mi);
}

/* create location trees */
//为了便于以后快速搜索location, 这里创建静态二叉平衡树
for (s = 0; s < cmcf->servers.nelts; s++) {

clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];

if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}

if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}

//初始化上面介绍到的ngx_http_core_main_conf_t类型结构体中的ngx_http_phase_t类型结构体.
//其实phase成员就表示一个数组, 数组中每个元素都是一个动态数组.
//每个动态数组中都存储着不同阶段的0个或多个ngx_http_handler_pt类型的模块处理方法.
//是否还记得刚开始学习NGINX模块开发时向HTTP某阶段添加模块时如何添加的模块处理方法 ?
//关于这点, 下面还会谈到
if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}

if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}

//在解析完配置文件后, 调用每个HTTP模块可能定义的postconfiguration方法.
//在postconfiguration方法中, 就可能是向上面ngx_http_phase_t类型成员中的某个阶段添加模块处理方法.
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = ngx_modules[m]->ctx;

if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}

...
//这里会处理ngx_http_core_main_conf_t结构体中的phase_engine成员. 该成员记录着每个HTTP模块的处理方法以及checker方法.
if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}

if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {...}
...
return rv;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: