1,HTTP模块初始化
(1)HTTP模块配置
http
{
include mime.types;
default_type application/octet-stream;
#charset gb2312;
server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;
client_max_body_size 8m;
sendfile on;
tcp_nopush on;
keepalive_timeout 60;
tcp_nodelay on;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;
#limit_zone crawler $binary_remote_addr 10m;
server
{
listen 80;
#server_name blog.s135.com;
index index.html index.htm index.php;
root /home/wwwroot/;
#limit_conn crawler 20;
location ~ .*\.(php|php5)?$
{
#fastcgi_pass unix:/tmp/php-cgi.sock;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
}
location ~ .*\.(js|css)?$
{
expires 1h;
}
log_format access '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';
access_log /usr/local/nginx-1.4.7/logs/access.log access;
}
server
{
listen 80;
server_name status.blog.s135.com;
location / {
stub_status on;
access_log off;
}
}
从上面的配置文件我们可以看到,HTTP的配置主要分为4层:
- 最外层的http{} 模块。模块类型:NGX_CORE_MODULE (最外层的核心模块)
- http核心模块中的main配置:http{include mine type; }。模块类型:NGX_HTTP_MODULE (全局的HTTP模块的配置信息)
- http核心模块中的server配置:server{}。模块类型:NGX_HTTP_MODULE (主要是配置Server的信息)
- http核心模块中的location本地信息配置:location{}。模块类型:NGX_HTTP_MODULE (主要一个server对应的本地资源信息:静态资源、反向代理端口地址、各种语言容器端口等)
最外层的http模块,类型NGX_CORE_MODULE,属于核心模块,核心模块在最开始配置文件初始化的时候,就会调用指令的命令集。所以在核心模块启动的时候就会调用http的模块配置解析指令函数:ngx_http_block
(2)HTTP核心模块的数据结构
/**
* HTTP模块命令集
* HTTP模块也是一个大模块,最外层为:
* http {
* ....
* }
* ngx_http_block:该方法就是回调函数
* HTTP核心模块
*/
static ngx_command_t ngx_http_commands[] = {
{ ngx_string("http"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_http_block,
0,
0,
NULL },
ngx_null_command
};
/**
*HTTP核心模块上下文
*/
static ngx_core_module_t ngx_http_module_ctx = {
ngx_string("http"),
NULL,
NULL
};
/**
* HTTP核心模块 结构
* 模块类型:NGX_CORE_MODULE
* 通过调用ngx_http_block方法,解析{}中的HTTP模块配置
*/
ngx_module_t ngx_http_module = {
NGX_MODULE_V1,
&ngx_http_module_ctx, /* module context */
ngx_http_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
从上面的结构中可以看到http{} 是NGX_CORE_MODULE,在核心模块初始化的时候,会调用ngx_http_commands命令集中的回调函数,逐个解析核心模块的配置信息。
而HTTP模块的总入口就是这个http{}命令集的回调函数:ngx_http_block
如果对配置文件如何解析有遗忘,请回顾《 Nginx源码分析 - 主流程篇 - 解析配置文件 》
(3)初始化流程及ngx_http_block函数解读
// ngx_http_core_module.h
// 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;
//
static ngx_command_t ngx_http_access_commands[] = {
{ ngx_string("allow"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|NGX_CONF_TAKE1,
ngx_http_access_rule,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("deny"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|NGX_CONF_TAKE1,
ngx_http_access_rule,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
//
static ngx_http_module_t ngx_http_access_module_ctx = {
NULL, /* preconfiguration */
ngx_http_access_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_access_create_loc_conf, /* create location configuration */
ngx_http_access_merge_loc_conf /* merge location configuration */
};
//
ngx_module_t ngx_http_access_module = {
NGX_MODULE_V1,
&ngx_http_access_module_ctx, /* module context */
ngx_http_access_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_init_cycle
--ngx_conf_param
--ngx_conf_parse
--ngx_conf_handler
--cmd->set(cf, cmd, conf)
--ngx_http_block
--module->create_main_conf(cf)/module->create_srv_conf(cf)/module->create_loc_conf(cf)
--module->preconfiguration(cf)/module->init_main_conf(cf, ctx->main_conf[mi]) //
--ngx_http_init_phases // 对http的11个阶段handlers分配内存空间
--module->postconfiguration(cf) // 设置http11个阶段的handler具体值
--ngx_http_access_init
--*h = ngx_http_access_handler;
--...
--
--ngx_http_init_phase_handlers // 设置http11个阶段的checker具体值
/**
*ngx_http_commands 命令集的回调函数
*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;
if (*(ngx_http_conf_ctx_t **) conf) {
return "is duplicate";
}
/* the main http context */
/* 分配一块内存,存放http配置上下文 */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
*(ngx_http_conf_ctx_t **) conf = ctx;
/* count the number of the http modules and set up their indices */
/* 计算http模块个数 */
ngx_http_max_module = ngx_count_modules(cf->cycle, NGX_HTTP_MODULE);
/* the http main_conf context, it is the same in the all http contexts */
/**
* 最外层的HTTP配置
* http
{
include mime.types;
default_type application/octet-stream;
*/
ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_http_max_module);
if (ctx->main_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* the http null srv_conf context, it is used to merge
* the server{}s' srv_conf's
*/
/**
* server层的配置
* server
{
listen 80;
#server_name blog.s135.com;
index index.html index.htm index.php;
root /home/wwwroot/;
*/
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* the http null loc_conf context, it is used to merge
* the server{}s' loc_conf's
*/
/**
* location 层的配置
location ~ .*\.(php|php5)?$
{
#fastcgi_pass unix:/tmp/php-cgi.sock;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}
*/
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* create the main_conf's, the null srv_conf's, and the null loc_conf's
* of the all http modules
*/
/**
* 调用:create_main_conf、create_srv_conf、create_loc_conf
* 创建配置
*/
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
mi = cf->cycle->modules[m]->ctx_index;
if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
if (ctx->main_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
if (ctx->srv_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
if (module->create_loc_conf) {
ctx->loc_conf[mi] = module->create_loc_conf(cf);
if (ctx->loc_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
}
pcf = *cf;
cf->ctx = ctx;
/**
* preconfiguration 预先初始化配置信息
*/
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
/* parse inside the http{} block */
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;
}
/*
* init http{} main_conf's, merge the server{}s' srv_conf's
* and its location{}s' loc_conf's
*/
/**
* 初始化main配置
* 合并 server srv_conf
* 合并location loc_conf
*/
cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
cscfp = cmcf->servers.elts;
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
mi = cf->cycle->modules[m]->ctx_index;
/* init http{} main_conf's */
if (module->init_main_conf) {
rv = module->init_main_conf(cf, ctx->main_conf[mi]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
rv = ngx_http_merge_servers(cf, cmcf, module, mi);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
/* create location trees */
/**
* 创建 location模块的trees
*/
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;
}
}
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;
}
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
if (ngx_http_variables_init_vars(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
/*
* http{}'s cf->ctx was needed while the configuration merging
* and in postconfiguration process
*/
*cf = pcf;
if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* optimize the lists of ports, addresses and server names */
/* ngx_http_optimize_servers 初始化listen 端口号 ip地址 服务器等监听信息*/
if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
failed:
*cf = pcf;
return rv;
}
2,HTTP处理流程概述
ngx_http_init_connection
ngx_http_wait_request_handler
ngx_http_wait_request_handler
ngx_http_process_request_line和ngx_http_process_request_headers
--ngx_http_process_request // 在接收到完整的HTTP头部后,已经有足有的必要信息开始在业务上处理HTTP请求了。这里开始11阶段。
--ngx_http_handler
--ngx_http_core_run_phases //处理http的11个阶段。根据ngx_http_core_main_conf初始化的checker,调用各阶段checker,各checker中会调用handler。
//其中:1,config_phase阶段是一个关键阶段,不可跳过。作用是根据rewrite_phase步骤重写后的URL检索出匹配的locaion。
//其中:2,content_phase阶段用于真正处理请求的内容是核心阶段。
//其一,content_phase以上9个阶段专注于4件基础性工作:rewrite重写URL,找到location配置块、判断是否有访问权限、try_files优先读取静态文件。
//其二,content_phase与其他阶段都不同的是,他向http模块提供两种接入该阶段的方式:第一种与其他10阶段相同,
//通过在全局的ngx_http_core_main_conf_t结构体中phases数组添加ngx_http_handler_pt处理方法来实现(也是http
//模块介入其他10阶段的唯一方法,是通过在必定会被调用的postconfiguration方法项全局gx_http_core_main_conf_t的
//phases[NGX_HTTP_CONTENT_PHASE]动态数组添加ngx_http_handler_pt处理方法来达成,这个处理方法将会应用于全部的HTTP请求)。
//而第二种是content_phase阶段独有,把handler处理方法设置到ngx_http_core_loc_conf_t中的handler指针中,
//如果请求匹配的location块下没有配置HTTP模块处理请求,那么这个handler为NULL(每个location对应一个
//独立的ngx_http_core_loc_conf_t,不必在postconfiguration设置handler,而是在ngx_command_t的某个配置项(如第3章
//中的mytest配置项)的回调方法中添加处理方法handler,好处是:不再应用于所有的http请求,仅仅当用户
//请求匹配了location时才会被调用)。
--ngx_http_core_find_config_phase
--ngx_http_core_find_location
--ngx_http_update_location_config{
if (clcf->handler) {
r->content_handler = clcf->handler;
}
}
--ngx_http_core_content_phase{
...
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;
}
rc = ph->handler(r);
...
}
- accept阶段(ngx_http_init_connection):
- 分配ngx_http_connection_t结构体;
- 根据fd的端口和地址,为hc->addr_conf赋值,以供后续server块查询;
- 新的fd加入epoll,设置读超时,超时时间为post_accept_timeout,设置连接可重用。
rev:ngx_http_wait_request_handler (此时没有读取任何数据。) wev:
ngx_http_empty_handler
- 首次读阶段(ngx_http_wait_request_handler):
- 分配c->buffer(cscf->client_header_buffer_size大小)用于存储客户端包头;
- recv调用接收包头;
- 创建ngx_http_request_t。
rev:ngx_http_process_request_line (循环接收请求行,解析请求行)
wev:ngx_http_empty_handler
- 请求行阶段(ngx_http_process_request_line):
- 循环recv,放入r->header_in(c->buffer);
- 解析uri,包括r->uri、r->args、r->exten;
- 请求行处理结束。
rev:ngx_http_process_request_headers; (循环接收请求头,解析请求头)
wev:ngx_http_empty_handler
- 请求头阶段(ngx_http_process_request_headers):
- 循环recv,放入r->header_in(c->buffer);
- 解析请求头,保存在r->headers_in.headers;
- 解析请求头结束后,调用ngx_http_process_request,进行11阶段和子请求处理。
rev:ngx_http_request_handler
——read_event_handler->ngx_http_block_reading (主请求读停止,子请求处理)
wev:ngx_http_request_handler
——write_event_handler->ngx_http_core_run_phases (11阶段,子请求处理)
- 十一阶段:(读触发)
- 在r->content_handler存在情况下,调用content_handler。
rev:ngx_http_request_handler
——read_event_handler->ngx_http_block_reading (子请求处理)
wev:ngx_http_request_handler
——write_event_handler->ngx_http_request_empty_handler (子请求处理)
- content_handler阶段:(ngx_http_proxy_handler)
- 分配ngx_http_upstream_t结构体,并根据上游的协议类型,为upstream分配不同的回调函数,例如:
create_request:创建上游请求
process_header:处理上游响应
finalize_request:结束上游请求
input_filter_init:input_filter初始化
input_filter:上游响应的处理函数 - 无请求体情况,直接进入上游初始化(ngx_http_upstream_init);
- 调用u->create_request将全部请求放入u->request_bufs(ngx_chain_t);
- 建立上游连接(ngx_http_upstream_connect)。
rev:ngx_http_upstream_handler
——read_event_handler->ngx_http_upstream_process_header (处理上游响应)
wev:ngx_http_upstream_handler
——write_event_handler->ngx_http_upstream_send_request_handler
(向上游发送请求)
-
request_body_no_buffering=0的情况(缓存请求体):
1.不调用ngx_http_upstream_init建立上游;
2.读事件触发请求体接收,直至请求体读取完成;
3.回调rb->post_handler,即建立上游。
rev:ngx_http_request_handler
——read_event_handler->ngx_http_read_client_request_body_handler(读取过程中)
——read_event_handler->ngx_http_block_reading(读取结束后) (读取请求体)
wev:ngx_http_request_handler
——write_event_handler->ngx_http_request_empty_handler (子请求处理)
- request_body_no_buffering=1的情况(不缓存请求体):
1.第一次读取结束后,设置r->reading_body = 1;
2.立即建立上游;
3.上游写事件触发时,将判断r->reading_body位,并不断读取请求体并发送至上游,直至请求体读取完成。
rev:ngx_http_request_handler
——read_event_handler->ngx_http_block_reading
(请求体下游读取一次后,后续的请求体不再由下游套接字触发接收,而是由上游的写事件触发。子请求处理)
wev:ngx_http_request_handler
——write_event_handler->ngx_http_request_empty_handler (子请求处理)
- r->discard_body = 1的情况:
rev:ngx_http_request_handler
——read_event_handler->ngx_http_discarded_request_body_handler
(请求体只读不发) wev:ngx_http_request_handler
——write_event_handler->ngx_http_request_empty_handler (子请求处理)
- 上游请求发送阶段:(ngx_http_upstream_send_request)
- 对于首次请求发送(!u->request_sent),调用ngx_output_chain,发送u->request_bufs和r->request_body->bufs中的请求,直到请求发送完毕;
- 非首次发送,只需要发送r->request_body->bufs。
- 如果request_body_no_buffering = 0,发送一次就结束。否则,将循环读取下游包体,并循环发送。
rev:ngx_http_upstream_handler
——read_event_handler->ngx_http_upstream_process_header (处理上游响应)
wev:ngx_http_upstream_handler
——write_event_handler->ngx_http_upstream_dummy_handler
(因请求发送完成,所以上游写事件不再处理)
-
响应头阶段:(ngx_http_upstream_process_header)
1.循环recv,放入u->buffer;
2.u->process_header解析响应行和响应头,解析结果保存在u->headers_in;
3.将响应头放入r->headers_out;
4.ngx_http_send_header发送响应头。
上游:
——read_event_handler->ngx_http_upstream_process_non_buffered_upstream-读,最终调用函数同下游写 (接收处理发送上游响应体) ——write_event_handler->ngx_http_upstream_dummy_handler
(不处理) 下游: ——read_event_handler->ngx_http_block_reading (不处理)
——write_event_handler->ngx_http_upstream_process_non_buffered_downstream-写,最终调用函数同上游读
(下游发送响应体)
-
响应体阶段:
即ngx_http_upstream_process_non_buffered_request调用
1.循环recv,放入u->buffer;
2.调用ngx_http_output_filter过滤及发送。
参考: