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

本文深入探讨了Nginx如何处理HTTP请求,包括启动过程中的初始化、连接处理、请求解析等步骤。详细讲解了Nginx的事件驱动模型、phase处理机制以及checker函数的实现。内容涵盖连接封装、读写事件处理、请求头解析和请求处理流程。通过附录提供了关键结构体、phase详解、初始化代码和checker函数代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

虽然我不想承认,但这篇文章的确是一篇很垃圾的博文。之所以垃圾 是因为没有考虑到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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值