在上一篇介绍了Nginx定义的11个阶段,本篇将深入介绍Nginx是如何处理HTTP请求的。
一、ngx_http_process_request
/**
* 处理HTTP请求
* @param r HTTP请求
* 当成功接收到请求行和Header时 就可以处理HTTP请求流程
* 至于是否存在body 则在后续流程中处理
*/
void ngx_http_process_request(ngx_http_request_t *r)
{
ngx_connection_t *c;
c = r->connection;
#if (NGX_HTTP_SSL)
...
#endif
//既然已经开始处理请求 表明没有发生超时事件 因此要及时删除定时器
if (c->read->timer_set)
{
ngx_del_timer(c->read);
}
#if (NGX_STAT_STUB)
...
#endif
/**
* 重新设置读写事件
* 为什么设置成ngx_http_request_handler:
* 进入此函数表示已经成功接收到HTTP请求行以及Header 后续再有读事件可能就是接收
* body。如果接收body只需要ngx_http_request_handler处理即可。 不需要再走过往
* 流程
*/
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
r->read_event_handler = ngx_http_block_reading; //对于epoll模型下 没有实际意义
/* 调度Nginx中所有HTTP模块协同处理该请求 */
ngx_http_handler(r);
/**
* 执行post请求 这里的post请求相当于子请求 与HTTP协议中post请求不一样
*/
ngx_http_run_posted_requests(c);
}
讲部分没有实际意义的代码删除掉,可以很清楚知道它的逻辑:
1、设置以后读写事件处理函数为ngx_http_request_handler,只针对当前请求来说。
2、调用所有HTTP模块协同处理当前请求,ngx_http_handler。
3、如果存在post请求,则处理post请求。这里可以理解成子请求。
二、ngx_http_handler
/**
* 调用所有HTTP模块协同处理请求
* @param r HTTP请求
*/
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;
#if (NGX_HTTP_GZIP)
r->gzip_tested = 0;
r->gzip_ok = 0;
r->gzip_vary = 0;
#endif
r->write_event_handler = ngx_http_core_run_phases; //设置写事件处理函数
ngx_http_core_run_phases(r);// 处理请求
}
三、执行流水线ngx_http_core_run_phases
/**
* 执行流水线 处理HTTP请求
* @param r HTTP请求
*/
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函数 然后再执行handler函数
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); //ngx_http_core_rewrite_phase ngx_http_core_find_config_phase ngx_http_core_generic_phase ngx_http_core_access_phase
if (rc == NGX_OK)
{
return;
}
}
}
四、checker函数定义
由于checker函数比较多,这里介绍两个checker函数,其中一个是比较经典的ngx_http_core_generic_phase,另外一个是我们经常用到的阶段NGX_HTTP_CONTENT_PHASE所对应的ngx_http_core_content_phase。
4.1、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;
/*
* generic phase checker,
* used by the post read and pre-access phases
*/
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"generic phase: %ui", r->phase_handler);
/**
* 调用回调函数
*/
rc = ph->handler(r);
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;
}
回到函数handler返回值说明:
返回值 | 说明 |
NGX_OK | 结束本阶段处理调到下一阶段。例如:在NGX_HTTP_ACCESS_PHASE阶段有两个HTTP模块A和B,都注册了该回调函数。当A模块的回调函数返回NGX_OK,则不会执行B模块定义的回调函数。而是跳到下一个阶段,如NGX_HTTP_POST_ACCESS_PHASE。 |
NGX_DECLINED | 继续执行本阶段中下一个处理函数。例如,在NGX_HTTP_ACCESS_PHASE阶段有两个HTTP模块A和B,都注册了该回调函数。当A模块的回调函数返回NGX_DECLINED,则执行B模块定义的回调函数,且阶段不变仍为NGX_HTTP_ACCESS_PHASE。 |
NGX_AGAIN NGX_DONE | 表示handler函数在一次调度中是无法完成相应的任务,需要多次调度。此时会把控制权交给事件驱动。 |
其他 | 直接结束当前HTTP请求。 |
4.2、ngx_http_core_content_phase
/**
* http content阶段处理函数
* @param r http请求
* @param ph handler对象
*/
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_update_location_config中.
* 取值实际为ngx_http_core_loc_conf_t中handler
* 这里:就是我们自定义回调函数入口
*/
if (r->content_handler)
{
r->write_event_handler = ngx_http_request_empty_handler;
ngx_http_finalize_request(r, r->content_handler(r));
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"content phase: %ui", r->phase_handler);
这个地方需要特别说明一下:我们在上一篇介绍了,NGX_HTTP_CONTENT_PHASE阶段特有的介入方法。Nginx为了访问方便,在ngx_http_request_t中增加了一个回调函数content_handler,其值实际为ngx_http_core_loc_conf_t中handler方法,可参考ngx_http_update_location_config函数。所以,当我们自定义了回调函数,这里就会执行自定义回调函数,否则执行默认方法。
rc = ph->handler(r);
if (rc != NGX_DECLINED)
{//结束HTTP请求
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
/* rc == NGX_DECLINED */
ph++;
if (ph->checker)
{//表示当前handler不是最后一个 继续执行下一个handler
r->phase_handler++;
return NGX_AGAIN;
}
/* no content handler was found */
/**
* 已经是最后一个handler
* 根据请求uri不一样 返回不同的响应
* 1) 根目录请求 返回403
* 2) 其他返回404
*/
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_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_OK;
}
五、总结
本篇以及上一篇介绍了Nginx是如何处理HTTP请求的。这里需要明确两点内容:
1、Nginx中11个阶段是如何管理以及流水线处理方式
2、Nginx中NGX_HTTP_CONTENT_PHASE阶段中,自定义回调函数是如何介入和调用的。
这里并没有把所有checker函数都介绍,主要原因是几乎每一个checker函数都很类似,只需要简单看一下就可以。如果仍然有不清楚的,可阅读《深入理解Nginx模块开发与架构解析》第二版,第11.6节内容。下一篇介绍处理HTTP body。