文章18 :Nginx中http请求的处理过程
这是我写的一篇关于Nginx中http请求处理的文章,里面参考了很多牛人的博客,由于本人当时疏忽,忘记了它们的网址,不能列举,还请见谅。
另由于本人能力有限,里面有很多地方肯定是不完善 或者是错误的,还请大家指正。
补充内容:
1.补充点:0序...2
1、基础知识...2
1.1 基本数据结构...2
1.2 http请求中phase的介绍...3
1.3 Nginx如何处理一个连接...3
1.3.1 基本介绍...3
1.3.2 Nginx如何处理一个连接...3
2.Nginx启动过程中的初始化...4
2.1监听ip地址与端口的确定...4
2.2phases[NGX_HTTP_LOG_PHASE + 1]的初始化...4
2.2.1理论说明:...4
2.2.2 详细说明:...5
2.2.3更深入说明:...6
2.3 checker的初始化...8
3.有http请求到达Nginx服务器...9
Nginx如何处理一个Http请求...9
3.1解析请求行...9
3.2解析请求头...10
3.3处理请求...10
3.4 流程图描述上述过程...11
3.5 对于ngx_http_core_run_phases的详细解释...13
3.6关于checker函数的实现...14
3.7 ngx_http_core_run_phases函数中的调用关系...16
附录列表...17
附录一 http请求中典型结构体...17
附录二 phase的详细说明...19
附录三 checker函数初始化完整代码...21
附录四 ngx_http_core_run_phases完整代码...25
附录五:checker函数完整代码...25
本文分析Nginx内部是如何对http请求进行响应的。主要分为Nginx启动、有http请求到达nginx服务器两部分进行说明。之所以分为两部分,是因为在Nginx启动过程中会有很多初始化的过程。这些内容接下来都会有详细介绍。
1、基础知识1.1 基本数据结构1)以图的形式给出, 请见图1
2)关于图中 ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE +1]; 以及checker的初始化,在后面的内容中会给出详细解释。
3)关于图中结构体的原型,详见附录一
4)注意内容:
(1)这儿要明白的是ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];phase数组有NGX_HTTP_LOG_PHASE+1个ngx_http_phase_t元素。其中每个ngx_http_phase_t元素对应于8个phase。对于每个ngx_http_phase_t元素呢,又有一个相应的ngx_array_t存放相应的handler。
(2)经过相应初始化后,ngx_http_phase_tphases[NGX_HTTP_LOG_PHASE + 1];包含所有phase的所对应的handler,并通过相应的关联,会将ngx_http_phase_handler_sphase_engine中的ngx_http_handler_pt handler指向ngx_http_phase_tphases[NGX_HTTP_LOG_PHASE + 1]该数组,也就是以后使用phase_engine->handlers
时就代指ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE+ 1]该数组。
Figure 1 http请求中的典型结构体
1.2 http请求中phase的介绍
1) nginx中的处理一个http请求分成了11个phase。在下面这个enum枚举类型中,我们看到了这11个phase。(不知道为何有的文章写8个),关于这几个phase的详细说明,请见附录2.
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;
2)更主要的是这几个phase的执行时严格按照顺序进行的,也就是NGX_HTTP_POST_READ_PHASE 是第一个,NGX_HTTP_LOG_PHASE是最后一个。
只有一个特殊的是NGX_HTTP_FIND_CONFIG_PHASE,因为在后面的rewrite phase中会改变uri,从而需要调用这个phase来实现通过uri来查找对应的location。
1.3 Nginx如何处理一个连接1.3.1 基本介绍在nginx中connection就是对tcp连接的封装,其中包括连接的socket,读事件,写事件。
nginx中对tcp连接的封装所用的结构体是structngx_connection_t结构体。
利用nginx封装的connection,我们可以很方便的使用nginx来处理与连接相关的事情,比如建立连接,发送与接收数据等。并且nginx中的http请求的处理就是建立在connection之上的。所以nginx不仅可以作为一个web服务器,也可以作为邮件服务器。当然,利用nginx提供的connection,我们可以与任何后端服务打交道。
1.3.2 Nginx如何处理一个连接1)Nginx作为服务器
(1)启动
首先,nginx在启动时,会解析配置文件,得到需要监听的端口与ip地址
然后,nginx的master进程里面,先初始化好这个监控的socket(创建socket--设置addrreuse等选项--绑定到指定的ip地址端口--在listen),然后再fork出多个子进程出来,然后子进程会竞争accept新的连接。
此时,客户端就可以向nginx发起连接了。
(2)客户端向Nginx发起连接
首先:当客户端与nginx进行三次握手,与nginx建立好一个连接后,此时,某一个子进程会accept成功,得到这个建立好的socket,然后创建nginx对连接的封装,即ngx_connection_t结构体。
其次:设置读写事件处理函数并添加读写事件来与客户端进行数据的交互。
最后,nginx或客户端来主动关掉连接。至此,一个连接寿终正寝。
2)Nginx作为客户端
Nginx也可以作为客户端来请求其他server的数据(如upstream模块),此时与其他server创建连接,所创建的连接也封装在ngx_connection_t结构体中。
作为客户端,
首先:nginx获取一个ngx_connection_t结构体
然后,创建socket,并设置socket属性(比如非阻塞)
之后,通过添加读写事件,调用connect/read/write来调用连接
最后,关掉连接,释放ngx_connection_t。
2.Nginx启动过程中的初始化2.1监听ip地址与端口的确定step1:ngx_event_process_init
1)为每一个监听套接字分配一个连接结构ngx_connection_t
2)设置读事件成员(read)的事件处理函数为ngx_event_accept函数
如果没有accept互斥锁,ngx_event_process_init会将读事件挂载nginx的事件处理模型
如果有accept互斥锁,则会等到initprocess阶段结束,在工作进程的事件处理循环中,某个进程抢到了accept互斥锁,才能挂载该读事件。
step2:当一个工作进程在某个时刻将监听事件挂载上事件处理模型之后,nginx就开始正式接受并处理客户端的请求了。nginx的事件处理模型接收到这个读事件之后,会速度交由之前注册好的事件处理函数ngx_event_accept来处理。:
step3:ngx_event_accept函数中,nginx调用accept函数,从已连接队列得到一个连接以及对应的套接字,接着分配一个连接结构(ngx_connection_t),并将新得到的套接字保存在该连接结构中,这里还会做一些基本的连接初始化工作。其中包括初始化读写事件的处理函数。
写事件的处理函数设置为ngx_http_empty_handler。这个事件处理函数不会做任何操作,实际上nginx默认连接第一次可写,不会挂载写事件,如果有数据需要发送,nginx会直接写到这个连接,只有在发生一次写不完的情况下,才会挂载写事件到事件模型上,并设置真正的写事件处理函数,这里后面的章节还会做详细介绍读事件的处理函数设置为ngx_http_init_request。
此时如果该连接上已经有数据过来(设置了deferred accept),则会直接调用 ngx_http_init_request函数来处理该请求,反之则设置一个定时器并在事件处理模型上挂载一个读事件,等待数据到来或者超时。当然这里不管是已经有数据到来,或者需要等待数据到来,又或者等待超时,最终都会进入读事件的处理函数-ngx_http_init_request。
2.2 phases[NGX_HTTP_LOG_PHASE + 1]的初始化接下来主要讲述ngx_http_core_main_conf_t中 ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];的初始化。
2.2.1理论说明:1) 所有的http请求都会调用模块配置文件中的函数ngx_http_block函数
2) 在该函数中会调用postconfiguration的函数,实现对handler的初始化,相应的handler都会被存入相应phase[NGX_HTTP_XXX_PHASE]的handler数组中。
3) 从而完成对phases[NGX_HTTP_LOG_PHASE+ 1];的初始化。
2.2.2 详细说明:1) 所有的http请求都会调用模块配置文件中的函数ngx_http_block函数
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
};
2) 在该函数中会调用postconfiguration的函数,实现对handler的初始化,相应的handler都会被存入相应phase[NGX_HTTP_XXX_PHASE]的handler数组中。
/*this code is in ngx_http_block in ngx_http.c*/
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
sum_http_module++; /*tested by yankai*/
module = ngx_modules[m]-> ctx;
if (module->postconfiguration ) {
sum_post_configuration++;/*tested by yankai*/
if (module->postconfiguration (cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
为了能够更好的说明handler是如何被初始化的,现在以ngx_http_access_module举例说明。
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 */
};
static ngx_int_t
ngx_http_access_init (ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_access_handler;
return NGX_OK;
}
在该ngx_http_module_t结构体中,postconfiguration是ngx_http_access_init函数,在该函数中通过ngx_array_push函数,将相应的handler处理函数加入到ngx_http_core_main_conf_t 的相应phases 数组中的handlers数组中。即phases数组包含phases[NGX_HTTP_SERVER_REWRITE_PHASE]~phases[NGX_HTTP_LOG_PHASE],并且在phases[NGX_HTTP_XXXX_XXX_PHASE]这个元素有对应于该phase的相应的handler函数。
3) 从而完成对phases[NGX_HTTP_LOG_PHASE+ 1];的初始化。
2.2.3更深入说明:现在将phase与handler的对应关系列举如下:
phase名称(加载模块调用postconfiguration时,存入顺序)
相对应的handler函数(加载模块调用postconfiguration时,存入顺序)
NGX_HTTP_LOG_PHASE
ngx_http_log_handler
NGX_HTTP_CONTENT_PHASE
ngx_http_static_handler
NGX_HTTP_CONTENT_PHASE
ngx_http_gzip_static_handler
NGX_HTTP_CONTENT_PHASE
ngx_http_autoindex_handler
NGX_HTTP_CONTENT_PHASE
ngx_http_index_handler
NGX_HTTP_ACCESS_PHASE
ngx_http_auth_basic_handler
NGX_HTTP_ACCESS_PHASE
ngx_http_access_handler
NGX_HTTP_PREACCESS_PHASE
ngx_http_limit_conn_handler
NGX_HTTP_PREACCESS_PHASE
ngx_http_limit_req_handler
NGX_HTTP_SERVER_REWRITE_PHASE
ngx_http_rewrite_handler
NGX_HTTP_REWRITE_PHASE
ngx_http_rewrite_handler
注意:
1) 我做了一个统计,共有42个http模块,其中有postconfiguration的是23个
2) ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];有phase数组有NGX_HTTP_LOG_PHASE +1个ngx_http_phase_t元素。其中每个ngx_http_phase_t元素对应于8个phase。对于每个ngx_http_phase_t元素呢,又有一个相应的ngx_array_t存放相应的handler。
3) 有很多http module可能没有postconfiguration,可能有postconfiguration但是没有相应的handler。现在列举如下
1)no post configuration:
ngx_http_browser_module.c
ngx_http_empty_gif_module.c
ngx_http_fastcgi_module.c
ngx_http_flv_module.c
ngx_http_geo_module.c
ngx_http_geoip_module.c
ngx_http_map_module.c
ngx_http_memcached_module.c
ngx_http_mp4_module.c
ngx_http_proxy_module.c
ngx_http_referer_module.c
ngx_http_scgi_module.c
ngx_http_secure_link_module.c
ngx_http_split_clients_module.c
ngx_http_ssl_module.c
ngx_http_stub_status_module.c
ngx_http_upstream_ip_hash_module.c
ngx_http_upstream_keepalive_module.c
ngx_http_upstream_least_conn_module.c
ngx_http_uwsgi_module.c
2)there is post configuration but no handler.
ngx_http_addition_filter_module.c
ngx_http_charset_filter_module.c
ngx_chunked_filter_module.c
ngx_http_headers_filter_module.c
ngx_http_image_filter_module.c
ngx_http_not_modified_filter_module.c
2.3 checker的初始化2.3.1理论说明
ngx_http_phase_engine_t中ngx_http_phase_handler_t * handlers 中的 ngx_http_phase_handler_pt checker 的checker函数的主要功能是:
1) 进行必要的校验操作
2) 调用相应phase的handler函数
3) 对handler返回的结果进行相应的处理
2.3.2详细解释
在ngx_http.c中ngx_http_block函数调用ngx_http_init_phase_handlers函数进行checker函数的初始化。部分代码如表2所示:详细代码详见附录3
Figure 2 checker函数初始化部分代码
/*this code is in ngx_http_block in ngx_http.c*/
if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
/*ngx_http_init_phase_handlers in ngx_http.c*/
switch (i) {
case NGX_HTTP_SERVER_REWRITE_PHASE :
checker = ngx_http_core_rewrite_phase;
break;
case NGX_HTTP_FIND_CONFIG_PHASE :
ph-> checker = ngx_http_core_find_config_phase;
case NGX_HTTP_REWRITE_PHASE :
checker = ngx_http_core_rewrite_phase;
case NGX_HTTP_POST_REWRITE_PHASE :
ph-> checker = ngx_http_core_post_rewrite_phase;
case NGX_HTTP_ACCESS_PHASE :
checker = ngx_http_core_access_phase;
case NGX_HTTP_POST_ACCESS_PHASE :
ph-> checker = ngx_http_core_post_access_phase; }
case NGX_HTTP_TRY_FILES_PHASE :
ph-> checker = ngx_http_core_try_files_phase;
case NGX_HTTP_CONTENT_PHASE :
checker = ngx_http_core_content_phase;
default:
checker = ngx_http_core_generic_phase;
}
存在问题:
关于phase数组中handler与ngx_http_phase_handler_s中的handler是如何对应的不明确。
为所有phase注册的handler链会被转换为ngx_http_phase_handler_s,然后保存在ngx_http_core_main_conf_t的phase_engine中。可能就是将ngx_http_phase_handler_s中的handler指向了phase数组,从而调用ngx_http_phase_handler_s 中的handler就直接调用phase数组
3.有http请求到达Nginx服务器Nginx如何处理一个Http请求
那接下来,简要讲讲nginx是如何处理一个完整的请求的。对于nginx来说,一个请求是从ngx_http_init_request开始的。
3.1解析请求行在ngx_http_init_request这个函数中,会设置读事件为ngx_http_process_request_line,也就是说,接下来的网络事件,会由ngx_http_process_request_line来执行。从ngx_http_process_request_line的函数名,我们可以看到,这就是来处理请求行的,正好与之前讲的,处理请求的第一件事就是处理请求行是一致的。通过ngx_http_read_request_header来读取请求数据。然后调用ngx_http_parse_request_line函数来解析请求行。
nginx为提高效率,采用状态机来解析请求行,而且在进行method的比较时,没有直接使用字符串比较,而是将四个字符转换成一个整形,然后一次比较以减少cpu的指令数,这个前面有说过。很多人可能很清楚一个请求行包含请求的方法,uri,版本,却不知道其实在请求行中,也是可以包含有host的。比如一个请求GET http://www.taobao.com/uri HTTP/1.0这样一个请求行也是合法的,而且host是www.taobao.com,这个时候,nginx会忽略请求头中的host域,而以请求行中的这个为准来查找虚拟主机。另外,对于对于http0.9版来说,是不支持请求头的,所以这里也是要特别的处理。所以,在后面解析请求头时,协议版本都是1.0或1.1。整个请求行解析到的参数,会保存到ngx_http_request_t结构当中。
3.2解析请求头在解析完请求行后,nginx会设置读事件的handler为ngx_http_process_request_headers,然后后续的请求就在ngx_http_process_request_headers中进行读取与解析。ngx_http_process_request_headers函数用来读取请求头,跟请求行一样,还是调用ngx_http_read_request_header来读取请求头,调用ngx_http_parse_header_line来解析一行请求头,解析到的请求头会保存到ngx_http_request_t的域headers_in中,headers_in是一个链表结构,保存所有的请求头。而HTTP中有些请求是需要特别处理的,这些请求头与请求处理函数存放在一个映射表里面,即ngx_http_headers_in,在初始化时,会生成一个hash表,当每解析到一个请求头后,就会先在这个hash表中查找,如果有找到,则调用相应的处理函数来处理这个请求头。比如:Host头的处理函数是ngx_http_process_host。
3.3处理请求当nginx解析到两个回车换行符时,就表示请求头的结束,此时就会调用ngx_http_process_request来处理请求了。
ngx_http_process_request会设置当前的连接的读写事件处理函数为ngx_http_request_handler,然后再调用ngx_http_handler来真正开始处理一个完整的http请求。这里可能比较奇怪,读写事件处理函数都是ngx_http_request_handler,其实在这个函数中,会根据当前事件是读事件还是写事件,分别调用ngx_http_request_t中的read_event_handler或者是write_event_handler。由于此时,我们的请求头已经读取完成了,之前有说过,nginx的做法是先不读取请求body,所以这里面我们设置read_event_handler为ngx_http_block_reading,即不读取数据了。刚才说到,真正开始处理数据,是在ngx_http_handler这个函数里面,这个函数会设置write_event_handler为ngx_http_core_run_phases,并执行ngx_http_core_run_phases函数。
ngx_http_core_run_phases这个函数将执行多阶段请求处理,nginx将一个http请求的处理分为多个阶段,那么这个函数就是执行这些阶段来产生数据。因为ngx_http_core_run_phases最后会产生数据,所以我们就很容易理解,为什么设置写事件的处理函数为ngx_http_core_run_phases了。
在这里,我简要说明了一下函数的调用逻辑,我们需要明白最终是调用ngx_http_core_run_phases来处理请求,产生的响应头会放在ngx_http_request_t的headers_out中,这一部分内容,我会放在请求处理流程里面去讲。
nginx的各种阶段会对请求进行处理,最后会调用filter来过滤数据,对数据进行加工,如truncked传输、gzip压缩等。这里的filter包括headerfilter与body filter,即对响应头或响应体进行处理。filter是一个链表结构,分别有header filter与body filter,先执行header filter中的所有filter,然后再执行body filter中的所有filter。在header filter中的最后一个filter,即ngx_http_header_filter,这个filter将会遍历所的有响应头,最后需要输出的响应头的一个连续的内存,然后调用ngx_http_write_filter进行输出。ngx_http_write_filter是body filter中的最后一个,所以nginx首先的body信息,在经过一系列的body filter之后,最后也会调用ngx_http_write_filter来进行输出。
这里要注意的是,nginx会将整个请求头都放在一个buffer里面,这个buffer的大小通过配置项client_header_buffer_size来设置,如果用户的请求头太大,这个buffer装不下,那nginx就会重新分配一个新的更大的buffer来装请求头,这个大buffer可以通过large_client_header_buffers来设置,这个large_buffer这一组buffer,比如配置4 8k,就是表示有四个8k大小的buffer可以用。注意,为了保存请求行或请求头的完整性,一个完整的请求行或请求头,需要放在一个连续的内存里面,所以,一个完整的请求行或请求头,只会保存在一个buffer里面。这样,如果请求行大于一个buffer的大小,就会返回414错误,如果一个请求头大小大于一个buffer大小,就会返回400错误。在了解了这些参数的值,以及nginx实际的做法之后,在应用场景,我们就需要根据实际的需求来调整这些参数,来优化我们的程序了。
3.4 流程图描述上述过程
3.5 对于ngx_http_core_run_phases的详细解释
该函数的核心部分如表3所示,完整代码详见附录4
Figure 3 ngx_http_core_run_phases部分代码
while (ph[r->phase_handler].checker) {
printf( "the number of checker is called is %d\n" ,i); /*edited by yankai*/
i++; /*edited by yankai*/
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
printf( "\t\t\t\t in ngx_http_core_run_phases the loop number is %d \n" ,i);
return ;
}
}
说明:
1) ngx_http_request_t *r中的一个成员变量phase_handler,用于确定是枚举类型ngx_http_phases中哪个phase。最开始时,r->phase_handler=0,随着r->phase_handler的递增,会顺序调用ngx_http_phases类型中的phase。这部分内容,已经经过我的验证,自己得出的结论,可能不完全正确,还请指正。
2) 如果该phase (phase[NGX_HTTP_XXXX_XXX_PHASE])的checker函数存在,那么就会调用这个phase的checker函数,并返回一个结果rc。
该checker函数的参数,param1 是ngx_http_request_t *r,param2是该phase[NGX_HTTP_XXXX_XXX_PHASE]本身
3)关于返回结果rc的介绍
(1)某个phase的checker==NGX_OK的话,代表当前的请求已经处理完毕
在这几个phase的checker中,它将所要执行的handler的返回值分为4种类型。
1、 NGX_OK 此时返回NGX_AGAIN,这里我们知道如果checker返回ok的话,整个handler的处理就会直接返回,也就是这次处理结束。并且这里phase_handler被赋值为ph->next,也就是下一个phase的索引。也就是说下次将会调用它的下一个phase的checker。
1. //处理NGX_OK
2. if (rc == NGX_OK) {
3. //下一个phase的索引
4. r->phase_handler = ph->next;
5. return NGX_AGAIN;
6. }
2、 NGX_DECLINED 此时也返回NGX_AGAIN,而这个和上面有所不同,那就是phase_handler的赋值,这里这个值只是简单的++,也就是说会紧接着处理当前phase的下一个phase,只有当前的phase的handelr处理完毕了,才可能会处理下一个phase的handler
1. //处理NGX_DECLINED
2. if (rc == NGX_DECLINED) {
3. //处理本phase的下一个handler
4. r->phase_handler++;
5. return NGX_AGAIN;
6. }
3、 NGX_AGAIN 或者NGX_DONE,这个的话直接返回OK,也就是会结束handler的处理。
1. //直接返回OK
2. if (rc == NGX_AGAIN || rc == NGX_DONE) {
3. return NGX_OK;
4. }
3.6关于checker函数的实现
对于不同phase的checker函数请通过上面内容中的ngx_http_init_phase_handlers 寻找
举例说明:以NGX_HTTP_CONTENT_PHASE 的checker函数是ngx_http_core_content_phase
表4为该checker函数部分代码,详细代码详见附录五
/*ngx_http_core_content_phase in ngx_http_core_module.c*/
ngx_int_t
ngx_http_core_content_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
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);
if (rc != NGX_DECLINED) {
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
ph++;
if (ph->checker) {
r->phase_handler++;
return NGX_AGAIN;
}
/* no content handler was found */
…..
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_OK;
}
解析:
1.在该函数中根据r->content_handler的不同做出了两种截然不同的处理。那么关于r->content_handler具体指什么呢?这是未知。
2.该函数前期主要做一些校验的操作,然后调用handler,最后对handler返回的结果做出一些相应的处理
3.关键部分就是handler的调用。
根据上面内容中写到的,对应于NGX_HTTP_CONTENT_PHASE的处理handler有如下四个,其初始化时的顺序为ngx_http_static_handler--ngx_http_gzip_static_handler---ngx_http_autoindex_handler---ngx_http_index_handler,
然而在调用handler时,其顺序刚好相反,即为ngx_http_index_handler---ngx_http_autoindex_handler---ngx_http_gzip_static_handler---ngx_http_static_handler。
NGX_HTTP_CONTENT_PHASE
ngx_http_static_handler
NGX_HTTP_CONTENT_PHASE
ngx_http_gzip_static_handler
NGX_HTTP_CONTENT_PHASE
ngx_http_autoindex_handler
NGX_HTTP_CONTENT_PHASE
ngx_http_index_handler
4.关于ngx_http_static_handler介绍(/*ngx_http_static_handlerin ngx_http_static_module.c*/)
主要功能:
1)设置response header和response body,
2)设置head_out,content_type,
3)最后调用ngx_http_send_header和ngx_http_output_filter函数
5. 关于ngx_http_index_handler介绍
主要功能:
1) 打开index指令配置文件的根文件index.html,
2) 如果httprequest header 的uri不以'/'结尾,返回NGX_DECLINED;
3) 如果以'/'结尾,就会在最后调用ngx_http_internal_redirect,该函数实现nginx的internalredirect的方式之一,修改了request header之后,就会重新调用ngx_http_handler。
存在问题:
1)什么是http request header的uri
2)在请求http://localhost/和http://localhost/时结果一样,都在最后调用ngx_http_internal_redirect,并重新调用ngx_http_handler
在请求http://localhost/hello,则没有调用ngx_http_internal_redirect,而是返回NGX_DECLINED。
3.7 ngx_http_core_run_phases函数中的调用关系
附录一 http请求中典型结构体
typedef struct {
/**
* edited by yankai
* 存储所有的ngx_http_core_srv_conf_t,元素的个数等于server块的个数。
*/
ngx_array_t servers; /* ngx_http_core_srv_conf_t */
/** * edited by yankai
* 包含所有phase,以及注册的phase handler,这些handler在处理 http请求时,
* 会被依次调用,通过ngx_http_phase_handler_t的next字段串联起来组成一个
* 链表。
*/
ngx_http_phase_engine_t phase_engine;
/** edited by yankai
* 以hash存储的所有request header
*/
ngx_hash_t headers_in_hash;
/** edited by yankai
* 被索引的 nginx变量,比如通过rewrite模块的set指令设置的变量,会在这个hash
* 中分配空间,而诸如$http_XXX和$cookie_XXX等内建变量不会在此分配空间。
*/
ngx_hash_t variables_hash;
/** edited by yankai
* ngx_http_variable_t类型的数组,所有被索引的 nginx变量被存储在这个数组中。
* ngx_http_variable_t结构中有属性index,是该变量在这个数组的下标。
*/
ngx_array_t variables; /* ngx_http_variable_t */
ngx_uint_t ncaptures;
/**
* server names的hash表的允许的最大bucket数量,默认值是512。
*/
ngx_uint_t server_names_hash_max_size;
/**
* server names的hash表中每个桶允许占用的最大空间,默认值是ngx_cacheline_size
*/
ngx_uint_t server_names_hash_bucket_size;
ngx_uint_t variables_hash_max_size;
ngx_uint_t variables_hash_bucket_size;
/**
* 主要用于初始化variables_hash变量。以hash方式存储所有的变量名,同时还保存
* 变量名和变量内容的 kv对的数组,ngx_hash_t就是以这个数组进行初始化的。这个
* 变量时临时的,只在解析配置文件时有效,在初始化variables_hash后,会被置为NULL。
*/
ngx_hash_keys_arrays_t * variables_keys;
/**
* 监听的所有端口,ngx_http_port_t类型,其中包含socket地址信息。
*/
ngx_array_t * ports;
ngx_uint_t try_files; /* unsigned try_files:1 */
/**
* 所有的phase的数组,其中每个元素是该phase上注册的handler的数组。
*/
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;
typedef struct {
/*
* edited by yankai
* 所有的 hanler都会在这个数组中. */
ngx_http_phase_handler_t * handlers ;
ngx_uint_t server_rewrite_index ;
ngx_uint_t location_rewrite_index ;
} ngx_http_phase_engine_t;
struct ngx_http_phase_handler_s { // ngx_http_phase_engine_t phase_engine---->ngx_http_phase_handler_t * handlers ;
/*
* edited by yankai
* checker 所有处于相同phase的handler的check都是相同的,
* 每个phase的handler的调用都是在check中的,
* 也就是check进行一些校验,结果判断等等操作。
* */
ngx_http_phase_handler_pt checker;
/*
* edited by yankai
* handler就是对应的handler处理函数
* */
ngx_http_handler_pt handler;
/*
* edited by yankai
* next 表示了下一个要执行的handler(也就是ngx_http_phase_handler_s)的位置,
* 由于是数组,所以这个也就表示数组索引。
* 而这个默认就是下一个将要执行的phase
* */
ngx_uint_t next;
};
typedef struct {
ngx_array_t handlers;
} ngx_http_phase_t;
附录二 phase的详细说明
/*this code is in ngx_http_core_module.h*/
typedef enum {
/*
* edited by yankai
* 读取请求phase */
NGX_HTTP_POST_READ_PHASE = 0,
/*
* edited by yankai
//接下来就是开始处理
//这个阶段主要是处理全局的(server block)的rewrite。
*/
NGX_HTTP_SERVER_REWRITE_PHASE,
/*
* edited by yankai
//这个阶段主要是通过 uri来查找对应的location。然后将 uri和location的数据关联起来
*/
NGX_HTTP_FIND_CONFIG_PHASE,
/*
* edited by yankai
//这个主要处理location的rewrite。
*/
NGX_HTTP_REWRITE_PHASE,
/*
* edited by yankai
//post rewrite,这个主要是进行一些校验以及收尾工作,以便于交给后面的模块。
*/
NGX_HTTP_POST_REWRITE_PHASE,
/*
* edited by yankai
//比如流控这种类型的access就放在这个phase,也就是说它主要是进行一些比较粗粒度的access。
*/
NGX_HTTP_PREACCESS_PHASE,
/*
* edited by yankai
//这个比如存取控制,权限验证就放在这个phase,一般来说处理动作是交给下面的模块做的.这个主要是做一些细粒度的access。
*/
NGX_HTTP_ACCESS_PHASE,
/*
* edited by yankai
//一般来说当上面的access模块得到access_code之后就会由这个模块根据access_code来进行操作
*/
NGX_HTTP_POST_ACCESS_PHASE,
/*
* edited by yankai
//try_file模块,也就是对应配置文件中的try_files指令。
*/
NGX_HTTP_TRY_FILES_PHASE,
/*
* edited by yankai
//内容处理模块,我们一般的handle都是处于这个模块
*/
NGX_HTTP_CONTENT_PHASE,
/*
* edited by yankai
//log模块
*/
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
附录三 checker函数初始化完整代码/*this code is in ngx_http_block in ngx_http.c*/
if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
/*ngx_http_init_phase_handlers in ngx_http.c*/
/*
* edited by yankai
//开始遍历phase handler.这里是一个phase一个phase的遍历。
*/
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
printf("\t\t\t\t\t ngx_http_init_phase_handlers 4\n" );/*edited by yankai*/
h = cmcf->phases[i]. handlers.elts ;
printf("取出对应的handler处理函数 h = cmcf->phases[i].handlers.elts;");/*edited by yankai*/
/*
* edited by yankai
//根据不同的phase来处理
*/
switch (i) {
case NGX_HTTP_SERVER_REWRITE_PHASE :
printf("\t\t\t\t\t ngx_http_init_phase_handlers 5\n" );/*edited by yankai*/
if (cmcf-> phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine. server_rewrite_index = n;
}
printf("\t\t\t\t\t set checker = ngx_http_core_rewrite_phase;\n" );/*edited by yankai*/
checker = ngx_http_core_rewrite_phase;
break;
case NGX_HTTP_FIND_CONFIG_PHASE :
printf("\t\t\t\t\t ngx_http_init_phase_handlers 6\n" );/*edited by yankai*/
find_config_index = n;
printf("\t\t\t\t\t set ph->checker = ngx_http_core_find_config_phase;\n" );/*edited by yankai*/
ph-> checker = ngx_http_core_find_config_phase;
n++;
ph++;
continue;
case NGX_HTTP_REWRITE_PHASE :
printf("\t\t\t\t\t ngx_http_init_phase_handlers 7\n" );/*edited by yankai*/
if (cmcf-> phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine. location_rewrite_index = n;
}
printf("\t\t\t\t\t set checker = ngx_http_core_rewrite_phase;\n" );/*edited by yankai*/
checker = ngx_http_core_rewrite_phase;
break;
case NGX_HTTP_POST_REWRITE_PHASE :
printf("\t\t\t\t\t ngx_http_init_phase_handlers 8\n" );/*edited by yankai*/
if (use_rewrite) {
printf("\t\t\t\t\t set ph->checker = ngx_http_core_post_rewrite_phase;\n" );/*edited by yankai*/
ph-> checker = ngx_http_core_post_rewrite_phase;
ph-> next = find_config_index;
n++;
ph++;
}
continue;
case NGX_HTTP_ACCESS_PHASE :
printf("\t\t\t\t\t ngx_http_init_phase_handlers 9\n" );/*edited by yankai*/
printf("\t\t\t\t\t set checker = ngx_http_core_access_phase;\n" );/*edited by yankai*/
checker = ngx_http_core_access_phase;
n++;
break;
case NGX_HTTP_POST_ACCESS_PHASE :
printf("\t\t\t\t\t ngx_http_init_phase_handlers 10\n" );/*edited by yankai*/
if (use_access) {
printf("\t\t\t\t\t set ph->checker = ngx_http_core_post_access_phase;\n" );/*edited by yankai*/
ph-> checker = ngx_http_core_post_access_phase;
ph-> next = n;
ph++;
}
continue;
case NGX_HTTP_TRY_FILES_PHASE :
printf("\t\t\t\t\t ngx_http_init_phase_handlers 11\n" );/*edited by yankai*/
if (cmcf-> try_files) {
printf("\t\t\t\t\t set ph->checker = ngx_http_core_try_files_phase;\n" );/*edited by yankai*/
ph-> checker = ngx_http_core_try_files_phase;
n++;
ph++;
}
continue;
case NGX_HTTP_CONTENT_PHASE :
printf("\t\t\t\t\t ngx_http_init_phase_handlers 12\n" );/*edited by yankai*/
printf("\t\t\t\t\t set checker = ngx_http_core_content_phase;\n" );/*edited by yankai*/
checker = ngx_http_core_content_phase;
break;
default:
printf("\t\t\t\t\t ngx_http_init_phase_handlers 13\n" );/*edited by yankai*/
printf("\t\t\t\t\t set checker = ngx_http_core_generic_phase;\n" );/*edited by yankai*/
checker = ngx_http_core_generic_phase;
}
附录四 ngx_http_core_run_phases完整代码
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
printf( "\t\t\t\t begin into ngx_http_core_run_phases\n" );/*edited by yankai*/
ngx_int_t rc;
ngx_http_phase_handler_t *ph;
ngx_http_core_main_conf_t *cmcf;
int i=0; /*edited by yankai*/
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers;
printf("r->phase_handler is %d to confirm which one\n\n",r->phase_handler);/*edited by yankai*/
while (ph[r->phase_handler].checker) {
printf( "the number of checker is called is %d\n" ,i);/*edited by yankai*/
i++; /*edited by yankai*/
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
printf( "\t\t\t\t in ngx_http_core_run_phases the loop number is %d \n" ,i);
return;
}
}
printf("\t\t\t\t end ngx_http_core_run_phases\n");/*edited by yankai*/
}
附录五:checker函数完整代码
/*ngx_http_core_content_phase in ngx_http_core_module.c*/
ngx_int_t
ngx_http_core_content_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
printf( "\t\t\tbegin into ngx_http_core_content_phase NGX_HTTP_CONTENT_PHASE\n");/*edited by yankai*/
size_t root;
ngx_int_t rc;
ngx_str_t path;
if (r->content_handler) {
printf( "\t\t\t there is r->content_handler ngx_http_core_content_phase 1\n" );/*edited by yankai*/
r->write_event_handler = ngx_http_request_empty_handler;
ngx_http_finalize_request(r, r->content_handler(r));
return NGX_OK;
}
printf("\t\t\t there is no r->content_handler ngx_http_core_content_phase 2\n");/*edited by yankai*/
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"content phase: %ui", r->phase_handler);
printf("\t\t\t ngx_http_core_content_phase 3\n");/*edited by yankai*/
rc = ph->handler(r);
printf("\t\t\t ngx_http_core_content_phase 4\n");/*edited by yankai*/
if (rc != NGX_DECLINED) {
printf( "\t\t\t ngx_http_core_content_phase 5\n" );/*edited by yankai*/
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
/* rc == NGX_DECLINED */
printf("\t\t\t ngx_http_core_content_phase 6\n");/*edited by yankai*/
ph++;
if (ph->checker) {
printf( "\t\t\t ngx_http_core_content_phase 7\n" );/*edited by yankai*/
r->phase_handler++;
printf( " \t\t\tin ngx_http_core_content_phase r->phase_handler is %d \n " ,r->phase_handler);/*edited by yankai*/
printf( "\t\t\t end ngx_http_core_content_phase\n" );/*edited by yankai*/
return NGX_AGAIN;
}
/* no content handler was found */
printf("\t\t\t ngx_http_core_content_phase 8\n");/*edited by yankai*/
if (r->uri.data[r->uri.len - 1] == '/') {
printf( "\t\t\t ngx_http_core_content_phase 9\n" );/*edited by yankai*/
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);
}
printf( "\t\t\t ngx_http_core_content_phase 10\n" );/*edited by yankai*/
ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
return NGX_OK;
}
printf("\t\t\t ngx_http_core_content_phase 11\n");/*edited by yankai*/
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
printf("\t\t\t ngx_http_core_content_phase 12\n");/*edited by yankai*/
return NGX_OK;
}