nginx 将处理 http 的请求分为 11 个阶段,每个阶段的作用不同,内置的一些模块都在不同的阶段实现它们的功能。这些阶段定义为如下的枚举类型:
typedef enum {
NGX_HTTP_POST_READ_PHASE = 0,
NGX_HTTP_SERVER_REWRITE_PHASE,
NGX_HTTP_FIND_CONFIG_PHASE,
NGX_HTTP_REWRITE_PHASE,
NGX_HTTP_POST_REWRITE_PHASE,
NGX_HTTP_PREACCESS_PHASE,
NGX_HTTP_ACCESS_PHASE,
NGX_HTTP_POST_ACCESS_PHASE,
NGX_HTTP_PRECONTENT_PHASE,
NGX_HTTP_CONTENT_PHASE,
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
第一个阶段是 POST_READ 阶段。ngx_http_realip_module
模块就工作在这个阶段,它可以根据 X-Forwarded-For
等头部,提取出客户端的真实 IP。
第二个阶段是 SERVER_REWRITE 阶段。ngx_http_rewrite_module
模块工作在这个阶段,用于实现 server{} 配置里的 url 重写功能。
第三个阶段是 FIND_CONFIG 阶段。没有模块能够介入这个阶段。这个阶段的功能是根据请求的 url 查找到匹配的 locaiton{} 。
第四个阶段是 REWRITE 阶段。ngx_http_rewrite_module
也工作在这个阶段,用于处理 location{} 配置里的 url 重写。
第五个阶段是 POST_REWRITE 阶段。没有模块能够介入这个阶段。这个阶段完全是为了配合 ngx_http_rewrite_module
模块。
第六个阶段是 PREACCESS 阶段。ngx_http_limit_conn_module
和 ngx_http_limit_req_module
模块工作在这个阶段,它们分别实现并发连接数限制和请求速率限制功能。
第七个阶段是 ACCESS 阶段。ngx_http_access_module
和 ngx_http_auth_basic_module
工作在这个阶段,它们分别实现访问控制和基本认证的功能。
第八个阶段是 POST_ACCESS 阶段。没有模块能够介入这个阶段。这个阶段是为了配合 ACCESS 阶段。
第九个阶段是 PRECONTENT 阶段。ngx_http_mirror_module
工作在这个阶段,实现流量镜像的功能。
第十个阶段是 CONTENT 阶段。这是最关键的一个阶段,也是外部模块最喜欢介入的阶段。ngx_http_index_module
、ngx_http_static_module
工作在这个阶段实现返回静态页面的功能。后面分析代码,我们也是重点关注这个阶段。
最后一个是 LOG 阶段。ngx_http_log_module
模块工作在这个阶段,实现输出访问日志的功能。
那么这些模块是怎样介入这些阶段呢?我们先看看 ngx_http_index_module
模块在解析完配置后会被调用的函数 ngx_http_index_init
。
static ngx_int_t
ngx_http_index_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, " ");
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_index_handler;
return NGX_OK;
}
它往 cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers 数组里添加了一个 handler ,这个 handler 是 ngx_http_index_handler
。这样在执行到这个阶段就会调用这个 handler 处理。
typedef struct {
...
ngx_http_phase_engine_t phase_engine;
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
...
} ngx_http_core_main_conf_t;
可以看到,core 模块创建的 main 级别的配置结构 ngx_http_core_main_conf_t
里有一个 phases 数组,它的大小是 11,每个阶段都用 ngx_http_phase_t
结构体表示。
typedef struct {
ngx_array_t handlers;
} ngx_http_phase_t;
我们看到这结构实际上就是一个数组,每一项都是一个函数指针,指向一个 handler 。它的原型是:
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
我们再看看另一个成员 phase_engine,它的结构是 ngx_http_phase_engine_t
。
typedef struct {
ngx_http_phase_handler_t *handlers;
ngx_uint_t server_rewrite_index;
ngx_uint_t location_rewrite_index;
} ngx_http_phase_engine_t;
它有一个 handlers 成员,指向一个 ngx_http_phase_handler_t
结构体数组,这个结构的定义如下:
struct ngx_http_phase_handler_s {
ngx_http_phase_handler_pt checker;
ngx_http_handler_pt handler;
ngx_uint_t next;
};
在这里我们发现了 handler 。
那么 phase_engine 成员是怎么初始化的呢?就来看看初始化它的函数 ngx_http_init_phase_handlers
。
ngx_http_init_phase_handlers
它被 ngx_http_block
调用,这时候各个模块已经添加了 handler。
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;
use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
n = 1 /* find config phase */
+ use_rewrite /* post rewrite phase */
+ use_access; /* post access 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;
}
cmcf->phase_engine.handlers = ph;
先计算出所有的 handler 数量,分配相应的内存。
n = 0;
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;
}
checker = ngx_http_core_rewrite_phase;
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;
}
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;
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_CONTENT_PHASE:
checker = ngx_http_core_content_phase;
break;
default:
checker = ngx_http_core_generic_phase;
}
n += cmcf->phases[i].handlers.nelts;
for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {
ph->checker = checker;
ph->handler = h[j];
ph->next = n;
ph++;
}
}
return NGX_OK;
然后根据每个阶段,设置 ngx_http_phase_handler_s
结构中的 checker、handler、next。
handler 指向各个模块添加的 handler 。
注意,里面的 for 循环是从后向前遍历的,所以后添加的 handler 先执行。
checker 指向 ngx_http_core_rewrite_phase
、ngx_http_core_find_config_phase
、ngx_http_core_post_rewrite_phase
、ngx_http_core_access_phase
、ngx_http_core_post_access_phase
、ngx_http_core_content_phase
、ngx_http_core_generic_phase
之一。因为每个阶段的作用不一样,所以需要指定不同的 checker,来根据 handler 的返回结果执行不同的流程。
每个 handler 在数组里的索引即是它的编号。
next 成员是下一个阶段的第一个 handler 的编号。这样,可以直接跳到下一个阶段执行。
注意,switch 语句里在处理 FIND_CONFIG 、POST_REWRITE 和 POST_ACCESS 阶段时,只设置了 checker,然后就 continue 了。所以这三个阶段只有 chcker,而没有 handler,模块是不能往这几个阶段添加 handler 的。
在循环的过程中,phase_engine成员中的 server_rewrite_index 的值被设置成 SERVER_REWRITE 阶段中重写 server{} 里 url 的 handler 的编号。server_rewrite_index 的值被设置成 REWRITE 阶段中重写 location{} 里 url 的 handler 的编号。这样,就能直接跳到这两个阶段执行了。
了解完初始化后,再看下处理流程的代码。
ngx_http_process_request
读取完请求后(不含包体),就进入请求处理阶段。由 ngx_http_process_request
函数开始。
void
ngx_http_process_request(ngx_http_request_t *r)
{
ngx_connection_t *c;
c = r->connection;
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
r->read_event_handler = ngx_http_block_reading;
ngx_http_handler(r);
ngx_http_run_posted_requests(c);
}
将连接的读写事件 handler 设置为 ngx_http_request_handler
。
将请求的读事件 handler 设置为 ngx_http_block_reading
。
然后调用 ngx_http_handler
函数处理请求。
最后调用 ngx_http_run_posted_requests
函数执行子请求。子请求的内容我会单独发一篇文章,这里就不提了。
ngx_http_request_handler
先看 ngx_http_request_handler
,它的代码很简单:
static void
ngx_http_request_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_http_request_t *r;
c = ev->data;
r = c->data;
ngx_http_set_log_request(c->log, r);
if (ev->delayed && ev->timedout) {
ev->delayed = 0;
ev->timedout = 0;
}
if (ev->write) {
r->write_event_handler(r);
} else {
r->read_event_handler(r);
}
ngx_http_run_posted_requests(c);
}
之后,当连接上出现读事件时,就调用请求的读事件 handler 。
当出现写事件时,就调用请求的写事件 handler 。
最后再执行子请求。
ngx_http_handler
void
ngx_http_handler(ngx_http_request_t *r)
{
ngx_http_core_main_conf_t *cmcf;
r->connection->log->action = NULL;
if (!r->internal) {
switch (r->headers_in.connection_type) {
case 0:
r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
break;
case NGX_HTTP_CONNECTION_CLOSE:
r->keepalive = 0;
break;
case NGX_HTTP_CONNECTION_KEEP_ALIVE:
r->keepalive = 1;
break;
}
r->lingering_close = (r->headers_in.content_length_n > 0
|| r->headers_in.chunked);
r->phase_handler = 0;
} else {
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
r->phase_handler = cmcf->phase_engine.server_rewrite_index;
}
r->valid_location = 1;
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r);
}
如果请求是外部的请求,则根据情况设置是否打开 Keepalive 功能。后面我单独发一篇文章分析 Keepalive 的实现。
然后将 phase_handler 设置为0,表示需要从第一个阶段 POST_READ 开始处理请求。
如果是内部的请求,则将 r->phase_handler 设置为 server_rewrite_index,表示需要从 SERVER_REWRITE 阶段开始处理请求。
然后将请求的写事件 handler 设置为 ngx_http_core_run_phases
,并调用它。这样,当请求不能一次处理完成,之后连接上出现写事件时,就调用它继续处理。
ngx_http_core_run_phases
这个函数调用每个阶段的 checker 执行它们的 handler。
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;
}
}
}
循环从编号为 r->phase_handler 的 handler 所在的阶段中开始执行。当它的 checker 返回 NGX_OK
时,退出循环,整个事件处理结束。否则,可能执行当前阶段的下一个 handler 或者跳到其他阶段执行。
下篇文章分析 CONTENT 阶段的 checker 函数 ngx_http_core_content_phase
,和 ngx_http_index_module
、ngx_http_static_module
这两个模块的 handler 代码。