本文共 10317 字,大约阅读时间需要 34 分钟。
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_process_request函数:
voidngx_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 voidngx_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函数中完成:
voidngx_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_tngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf);static ngx_int_tngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
这两个函数都是在ngx_http_block函数中被调用的。
static ngx_int_tngx_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_PHASE2. NGX_HTTP_SERVER_REWRITE_PHASE3. NGX_HTTP_REWRITE_PHASE4. NGX_HTTP_PREACCESS_PHASE5. NGX_HTTP_ACCESS_PHASE6. NGX_HTTP_CONTENT_PHASE7. NGX_HTTP_LOG_PHASE
这个函数中主要是用上一个函数,以及各个http模块通过调用自己的postconfiguration函数初始化后的cmcf->phase二维数组对cmcf->phase_engine.handlers数组进行初始化,cmcf->phase_engine.handlers是处理http请求是实际使用的东西。这里需要注意的是,cmcf->phase_engine.handlers是一个一维数组。下面我们具体看一下这个函数的实现:
//为cmcf->phase_engine.handlers分配空间static ngx_int_tngx_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_PHASE2.NGX_HTTP_POST_REWRITE_PHASE3.NGX_HTTP_POST_ACCESS_PHASE4.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请求中的具体作用。
转载地址:http://dpqxi.baihongyu.com/