文章18 :Nginx中http请求的处理过程

来源:互联网 发布:网易广州 知乎 编辑:程序博客网 时间:2024/05/21 09:58

虽然我不想承认,但这篇文章的确是一篇很垃圾的博文。之所以垃圾 是因为没有考虑到Nginx的事件驱动对于请求处理的影响。建议各位看官去阅读

 《http://tengine.taobao.org/book/index.html》和《深入理解Nginx--陶辉》的第11章内容。

这是我写的一篇关于Nginx中http请求处理的文章,里面参考了很多牛人的博客,由于本人当时疏忽,忘记了它们的网址,不能列举,还请见谅。

另由于本人能力有限,里面有很多地方肯定是不完善 或者是错误的,还请大家指正。

 补充内容:

1.补充点:

如果不指定ngx_http_core_loc_conf_t ->handler,那么请求转发到默认的content phase中的handler(如ngx_http_index_handler,ngx_http_static_handler);但是如果指定了ngx_http_core_loc_conf_t ->handler,那么请求就会转发到该指定的handler中处理,生成内容。hello_world模块是后者,http://localhost输出welcome to Nginx!是前者。可以在函数内部输入printf,看输出,就可以更加明确这一点。
2.补充点2:
消息体的处理,Nginx的消息体的处理属于异步处理。真正处理消息体是在ngx_http_read_client_request_body函数中进行处理的。通过ngx_http_read_client_request_body和ngx_http_do_read_client_request_body函数,进行消息体的相关处理。过一段时间我会将这部分内容做一个补充。

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结构体中,postconfigurationngx_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。现在列举如下

1no 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数组中handlerngx_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 headeruri

    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_tnext字段串联起来组成一个

       * 链表。

       */

    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 nameshash表的允许的最大bucket数量,默认值是512

        */

    ngx_uint_t                 server_names_hash_max_size;

    /**

         * server nameshash表中每个桶允许占用的最大空间,默认值是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  所有处于相同phasehandlercheck都是相同的,

                 * 每个phasehandler的调用都是在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。然后将 urilocation的数据关联起来

    */

    NGX_HTTP_FIND_CONFIG_PHASE,

    /*

             * edited by yankai

            //这个主要处理locationrewrite

     */

    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_engineserver_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_enginelocation_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;

}

 

 


原创粉丝点击