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
0序
本文分析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时,存入顺序) | |
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; } |