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

Nginx源码剖析--HTTP请求的分阶段处理的初始化

2017-11-16 15:42 555 查看

前言

Nginx作为一个http服务器,核心任务就是处理HTTP请求。在接收到请求时,Nginx框架首先解析http请求,将解析结果放在ngx_http_request中,由于http是在tcp上工作的,因此解析可能会持续一段时间。nginx用状态机完成对HTTP请求的异步解析。整个解析过程都是由Nginx框架代码完成,不需要用户介入。当解析得到完整的http请求后,就开始处理http请求。nginx对http请求的处理是分阶段进行的。对请求的处理也是用户主要介入nginx的地方。对于一个http请求,nginx主要分以下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_TRY_FILES_PHASE,
NGX_HTTP_CONTENT_PHASE,

NGX_HTTP_LOG_PHASE
} ngx_http_phases;


这篇博文将简单介绍一下这些处理请求时,这些阶段是怎么介入进去的。

http请求的处理 – ngx_http_handler

http请求处理的入口是ngx_http_process_request函数:

void
ngx_http_process_request(ngx_http_request_t *r)


在ngx_http_process_request函数中,我们可以看到:

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_request_handler处理函数。

static void
ngx_http_request_handler(ngx_event_t *ev)
{
ngx_connection_t    *c;
ngx_http_request_t  *r;
ngx_http_log_ctx_t  *ctx;

c = ev->data;
r = c->data;

ctx = c->log->data;
ctx->current_request = r;

ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http run request: \"%V?%V\"", &r->uri, &r->args);

if (ev->write) {
r->write_event_handler(r); //ngx_http_core_run_phases

} else {
r->read_event_handler(r);
}

ngx_http_run_posted_requests(c);
}


我们可以看到这个函数主要是通过调用r->read_event_handler和r->write_event_handler来完成任务。

而从ngx_http_process_request里面可以看到:

r->read_event_handler = ngx_http_block_reading;


r->write_event_handler在ngx_http_handler函数中被设置:

r->write_event_handler = ngx_http_core_run_phases;


由于处理http请求主要是处理写事件,因为接到http请求之后,后面需要做的就是将响应写到客户端。

因此我们可以看到,nginx处理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); //获得ngx_http_core_module模块的main_conf配置结构体

ph = cmcf->phase_engine.handlers;

while (ph[r->phase_handler].checker) { //逐一执行各个阶段的checker函数

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

if (rc == NGX_OK) {
return;
}
}
}


可以看到,Nginx依次调用各个阶段的checker函数处理请求。请求的当前处理阶段是通过

r->phase_handler


来标识。

各个阶段处理函数的初始化

从前面可以知道,各个阶段的处理函数都在ngx_http_core_module模块的main_conf的phase_engine.handlers成员中。phase_engine.handlers是一个数组,数组类型是

struct ngx_http_phase_handler_s {
ngx_http_phase_handler_pt  checker;
ngx_http_handler_pt        handler;
ngx_uint_t                 next;
};


也就是说,每个元素包含三个成员,分别是checker函数,handler函数,和next。checker函数是对外的接口,handler一般是由checker函数调用,next表示下一个阶段被调用的首个处理函数在数组中的位置。因此对各个阶段处理函数的初始化就是对phase_engine.handlers是一个数组的初始化。这个初始化和两个函数相关:

static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf);

static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)


这两个函数都是在ngx_http_block函数中被调用的。

ngx_http_init_phases

static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}

if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}

if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}

if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}

if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
cf->pool, 2, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}

if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
cf->pool, 4, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}

if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}

return NGX_OK;
}


这个函数很简单,主要是对几个数组进行初始化。

ngx_http_core_module的main_conf配置结构体中的phases是一个数组

ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];


数组中的每个元素的类型是

typedef struct {
ngx_array_t                handlers;
} ngx_http_phase_t;


所以phases是一个二维数组。phase数组中的每个元素表示对应一个阶段的handler函数数组。这是为了后面初始化phase_engine.handlers做准备的。

Nginx模块编写用户可以通过向具体的阶段添加handler函数来实现http请求的处理干涉。一般的方法就是向phases中对应的某个阶段的handler数组中添加。如要向第( i )阶段添加一个handler,则将handler函数 push 到phases[i]数组中就可以了,这个介入一般是通过调用http模块的postconfiguration成员函数实现。

我们从这个函数可以看到,函数中只初始化了7个阶段对应的数组,而不是所有的11个阶段的数组都被初始化。因此,可以发现,Nginx当前只支持用户介入处理http请求的七个阶段:

1. NGX_HTTP_POST_READ_PHASE
2. NGX_HTTP_SERVER_REWRITE_PHASE
3. NGX_HTTP_REWRITE_PHASE
4. NGX_HTTP_PREACCESS_PHASE
5. NGX_HTTP_ACCESS_PHASE
6. NGX_HTTP_CONTENT_PHASE
7. NGX_HTTP_LOG_PHASE


ngx_http_init_phase_handlers

这个函数中主要是用上一个函数,以及各个http模块通过调用自己的postconfiguration函数初始化后的cmcf->phase二维数组对cmcf->phase_engine.handlers数组进行初始化,cmcf->phase_engine.handlers是处理http请求是实际使用的东西。这里需要注意的是,cmcf->phase_engine.handlers是一个一维数组。下面我们具体看一下这个函数的实现:

//为cmcf->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;

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 = 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;
}

cmcf->phase_engine.handlers = ph;


这部分代码的主要功能是为cmcf->phase_engine.handlers分配空间。其他功能我们在后面详细介绍各个阶段的功能时在说。

//用cmcf->phase二维数组对cmcf->phase_engine.handlers数组进行初始化
n = 0;

for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
//h指向当前阶段的handler数组
h = cmcf->phases[i].handlers.elts;

switch (i) {
// 凡是有continue语句的,表示这个阶段的处理方法只能由HTTP框架实现
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_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 += cmcf->phases[i].handlers.nelts;

for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) { //in reversed order
ph->checker = checker;
ph->handler = h[j];
ph->next = n;
ph++;
}
}


这段代码很简单,就是依次将每个阶段的handler放在cmcf->phase_engine.handlers中。

这里讲一下几点。首先可以看到switch语句中对于有些阶段会用break,有些阶段会用continue。用continue表示对于当前阶段不会走到后面的for循环,也就是说,不会将continue对应的阶段的handler函数加入到cmcf->phase_engine.handlers中。换句话说,Nginx不支持往这些阶段中添加handler函数。包括以下四个阶段

1.NGX_HTTP_FIND_CONFIG_PHASE
2.NGX_HTTP_POST_REWRITE_PHASE
3.NGX_HTTP_POST_ACCESS_PHASE
4.NGX_HTTP_TRY_FILES_PHASE


对应地在处理请求时,只执行它的checker函数。

我们知道cmcf->phase_engine.handlers数组每个成员的类型是:

struct ngx_http_phase_handler_s {
ngx_http_phase_handler_pt  checker;
ngx_http_handler_pt        handler;
ngx_uint_t                 next;
};


从代码可以看到,next始终指向下一个阶段的第一个handler在cmcf->phase_engine.handlers中的位置。因此next提供了一种忽略当前阶段的其他handler直接进入下一阶段handler处理请求的方式:

r->phase_handler= next;


(注:每个阶段由多个handler组成,同一个阶段的所有handler在cmcf->phase_engine.handlers数组中出于连续的位置上)

而继续用当前阶段的handler处理则:

r->phase_handler++;


r->phase_handler一般是在各个checker函数中被改变的。而checker函数根据handler函数的返回结果改变这个值。

(注:相同阶段的所有handler对应的checker函数是一样的)。

总结

本文介绍了cmcf->phase_engine.handlers的初始化过程。cmcf->phase_engine.handlers是一个一维数组,它将所有阶段的handler存储起来,相同阶段的handler在cmcf->phase_engine.handlers中的位置相连续。在处理http请求时,依次调用cmcf->phase_engine.handlers中的handler(通过对应的checker调用handler),通过handler中的next来实现从一个阶段跳跃到下一个阶段的处理。处理请求时,主要是通过请求的r->phase_handler来标识这个请求当前被cmcf->phase_engine.handlers中的哪个handler处理。

这样实现了http请求的多阶段异步处理。

这里没有详细介绍各个阶段的功能,我们后面再详细介绍各个阶段在处理http请求中的具体作用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: