Nginx 中 HTTP 模块初始化

概述

在前面的文章《 Nginx 配置解析》简单讲解了通用模块的配置项解析,并且大概讲解了HTTP 模块的配置项解析过程,本文更具体的分析HTTP 模块的初始化过程。HTTP 模块初始化过程主要有:上下文结构初始化、配置项解析、配置项合并、server 相关端口设置。

HTTP 模块接口

ngx_http_module_t 结构体

在 Nginx 中,结构体 ngx_module_t 是 Nginx 模块最基本的接口。对于每一种不同类型的模块,都有一个具体的结构体来描述这一类模块的通用接口。在Nginx 中定义了HTTP 模块的通用接口ngx_http_module_t 结构体,该结构体定义在文件src/http/ngx_http_config.h:我们把直属于http{}、server{}、location{} 块的配置项分别称为main、srv、loc 级别配置项。


typedef struct {
    
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
    
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);

    
    void       *(*create_main_conf)(ngx_conf_t *cf);
    
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    
    void       *(*create_srv_conf)(ngx_conf_t *cf);
    
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

    
    void       *(*create_loc_conf)(ngx_conf_t *cf);
    
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

ngx_http_conf_ctx_t 结构体

在 HTTP 模块中,管理 HTTP 模块配置项的结构由 ngx_http_conf_ctx_t 实现,该结构有三个成员,分别指向三个指针数组,指针数组是由相应地 HTTP 模块create_main_conf、create_srv_conf、create_loc_conf 方法创建的结构体指针组成的数组。ngx_http_conf_ctx_t 结构体定义在文件src/http/ngx_http_config.h 中:

ngx_http_module 核心模块

ngx_http_module 核心模块定义

ngx_http_module 是 HTTP 模块的核心模块,该模块的功能是:定义新的 HTTP 模块类型,并为每个HTTP 模块定义通用接口ngx_http_module_t 结构体,管理HTTP 模块生成的配置项结构体,并解析HTTP 类配置项。该模块定义在文件src/http/ngx_http.c 中:

ngx_http_module 作为核心模块,必须定义核心模块的通用接口上下文结构,其通用接口上下文结构定义在文件src/http/ngx_http.c 中:在 ngx_http_module 核心模块中只定义了 http 模块的名称。

在 ngx_http_module 模块中定义了http{} 块感兴趣的配置项数组,配置项数组定义在文件src/http/ngx_http.c 中:

从 ngx_http_module 模块的定义中可以知道,该模块只有一个初始化处理方法ngx_http_block,该处理方法是HTTP 模块的初始化作用。

ngx_http_module 核心模块初始化

在上面 ngx_http_module 模块的定义中已经知道,HTTP 模块的初始化过程由函数ngx_http_block 实现,首先先给出该函数的整体分析,接着对该函数进行具体的分析。该函数定义在文件src/http/ngx_http.c 中:

从上面的分析中可以总结出 HTTP 模块初始化的流程如下:

  • Nginx 进程进入主循环,在主循环中调用配置解析器解析配置文件nginx.conf;

  • 在配置文件中遇到 http{} 块配置,则 HTTP 框架开始初始化并启动,其由函数 ngx_http_block() 实现;

  • HTTP 框架初始化所有 HTTP 模块的序列号,并创建 3 个类型为 ngx_http_conf_ctx_t 结构的数组用于存储所有HTTP 模块的create_main_conf、create_srv_confcreate_loc_conf方法返回的指针地址;

  • 调用每个 HTTP 模块的 preconfiguration 方法;

  • HTTP 框架调用函数 ngx_conf_parse() 开始循环解析配置文件 *nginx.conf *中的http{}块里面的所有配置项,http{} 块内可嵌套多个server{} 块,而 server{} 块可嵌套多个 location{},location{} 依旧可以嵌套location{},因此配置项解析函数是递归调用;

  • HTTP 框架处理完毕 http{} 配置项,根据解析配置项的结果,必要时调用ngx_http_merge_servers 方法进行配置项合并处理,即合并main、srv、loc 级别下server、location 相关的配置项;

  • 初始化可添加处理方法的 HTTP 阶段的动态数组;

  • 调用所有 HTTP 模块的 postconfiguration 方法使 HTTP 模块可以处理HTTP 阶段,即将HTTP 模块的ngx_http_handler_pt 处理方法添加到HTTP 阶段中;

  • 根据 HTTP 模块处理 HTTP 阶段的方法构造 phase_engine_handlers 数组;

  • 构造 server 相关的监听端口,并设置新连接事件的回调方法为ngx_http_init_connection ;

  • 继续处理其他 http{} 块之外的配置项,直到配置文件解析器处理完所有配置项后通知Nginx 主循环配置项解析完毕。此时,Nginx 才会启动Web 服务器;

ngx_http_core_module 模块

ngx_http_core_main_conf_t 结构体

在初始化 HTTP 模块的过程中,会调用配置项解析函数ngx_conf_parse 解析http{} 块内的配置项,当遇到server{} 块、location{} 块配置项时,此时会调用配置项解析函数解析server{} 和location{} 块,在解析这两个配置块的过程中依旧会创建属于该块的配置项结构srv_conf、loc_conf,因此就会导致与http{} 块所创建的配置项结构体重复,这时候就需要对这些配置项进行管理与合并。首先先看下结构体ngx_http_core_main_conf_t,ngx_http_core_main_conf_t 是ngx_http_core_module 的 main_conf,存储了 http{} 层的配置参数。该结构体定义在文件src/http/ngx_http_core_module.h中:

ngx_http_core_srv_conf_t 结构体

结构体 ngx_http_core_srv_conf_t,ngx_http_core_srv_conf_t 是ngx_http_core_module 的srv_conf,存储了server{} 层的配置参数。该结构体定义在文件src/http/ngx_http_core_module.h中:

ngx_http_core_loc_conf_t 结构体

结构体 ngx_http_core_loc_conf_t,ngx_http_core_loc_conf_t 是ngx_http_core_module 的loc_conf,存储了location{} 层的配置参数。该结构体定义在文件src/http/ngx_http_core_module.h中:

ngx_http_core_module 模块定义

ngx_http_core_module 模块是HTTP 模块中的第一个模块,该模块管理srv、loc 级别的配置项结构。该模块在文件src/http/ngx_http_core_module.c中定义:

在模块的定义中,其中定义了 HTTP 模块的上下文结构ngx_http_core_module_ctx,该上下文结构体定义如下:

由于该模块中感兴趣的配置项太多,这里只列出 server、location 配置项。定义如下:

管理 HTTP 模块不同级别的配置项结构体

在 HTTP 模块的 http{} 配置项解析过程中,可能遇到多个嵌套 server{} 块以及location{},不同块之间的解析都会创建相应的结构体保存配置项参数,但是由于属于嵌套关系,所有必须管理好不同块之间的配置项结构体,方便解析完毕后合并相应的配置项,以下针对不同级别的配置项结构体进行分析。

获取不同级别配置项结构

根据不同结构体变量的参数获取不同级别的配置项结构体由宏定义实现,在文件 src/http/ngx_http_config.h中定义如下:

main 级别的配置项结构体

在 ngx_http_module 模块 http{} 块配置项解析的初始化过程中由函数 ngx_http_block 实现,在实现过程中创建并初始化了HTTP 模块main 级别的配置项main_conf、srv_conf、loc_conf 结构体。main 级别的配置项结构体之间的关系如下图所示:

server 级别的配置项结构体

在 ngx_http_module 模块在调用函数ngx_conf_parse 解析 http{} 块main 级别配置项时,若遇到server{} 块配置项,则会递归调用函数ngx_conf_parse 解析ngx_http_core_module 模块中 server{} 块配置项,并调用方法ngx_http_core_server 初始化server{} 块 ,该方法创建并初始化了HTTP 模块srv 级别的配置项srv_conf、loc_conf 结构体。server{} 块配置项的初始化函数创建配置项结构体的源码如下所示:

srv 级别的配置项结构体之间的关系如下图所示:

location 级别的配置项结构体

在 ngx_http_core_module 模块在调用函数ngx_conf_parse 解析 server{} 块srv 级别配置项时,若遇到 location{} 块配置项,则会递归调用函数ngx_conf_parse 解析ngx_http_core_module 模块中 location{} 块配置项,并调用方法ngx_http_core_location 初始化location{} 块 ,该方法创建并初始化了HTTP 模块loc 级别的配置项loc_conf 结构体。location{} 块配置项的初始化函数创建配置项结构体的源码如下所示:

loc 级别的配置项结构体之间的关系如下图所示:若 location 是精确匹配、正则表达式、@命名则exact 字段有效,否则就是 inclusive 字段有效,画图过程中只画出exact 字段有效。

合并配置项

HTTP 框架解析完毕 http{} 块配置项时,会根据解析的结果进行合并配置项操作,即合并 http{}、server{}、location{} 不同级别下各 HTTP 模块生成的存放配置项的结构体。其合并过程在文件src/http/ngx_http.c中定义,如下所示:

  • 若 HTTP 模块实现了 merge_srv_conf 方法,则将 http{} 块下由 create_srv_conf 生成的 main 级别结构体与遍历每一个 server{}块下由 create_srv_conf生成的srv 级别的配置项结构体进行 merge_srv_conf 操作;

  • 若 HTTP 模块实现了 merge_loc_conf 方法,则将 http{} 块下由 create_loc_conf 生成的 main 级别的配置项结构体与嵌套在每一个server{} 块下由 create_loc_conf 生成的srv级别的配置项结构体进行merge_loc_conf 操作;

  • 若 HTTP 模块实现了 merge_loc_conf 方法,由于在上一步骤已经将main、srv级别由create_loc_conf 生成的结构体进行合并,只要把上一步骤合并的结果在 server{} 块下与嵌套每一个location{}块下由create_loc_conf 生成的配置项结构体再次进行merge_loc_conf 操作;

  • 若 HTTP 模块实现了 merge_loc_conf 方法,则将上一步骤的合并结果与与嵌套每一个location{}块下由 create_loc_conf 生成的的配置项结构体再次进行merge_loc_conf 操作;

具体合并过程如下图所示:

HTTP 请求处理阶段

按照下列顺序将各个模块设置的phase handler依次加入cmcf->phase_engine.handlers列表,各个phase的phase handler的checker不同。checker主要用于限定某个phase的框架逻辑,包括处理返回值。 在Nginx 定义了 11 个处理阶段,有一部分是不能添加 phase handler 方法的。在文件src/http/ngx_http_core_module.h中定义,如下所示:

每个HTTP的checker方法与handler处理如下所示:

完成 http{} 块的解析后,根据 *nginx.conf *文件中配置产生由 ngx_http_phase_handler_t 组成的数组,在处理 HTTP 请求时,一般情况下按照阶段的方向顺序 phase handler 加入到回调表中。ngx_http_phase_engine_t 结构体由所有ngx_http_phase_handler_t 组成的数组,如下所示:

ngx_http_phase_engine_t 中保存在当前nginx.conf 配置下,一个用户请求可能经历的所有 ngx_http_handler_pt 处理方法。

在 HTTP模块初始化过程中,HTTP模块通过postconfiguration方法将自定义的方法添加到handler数组中,即该方法会被添加到phase_engine数组中。下面以NGX_HTTP_POST_READ_PHASE阶段为例,讲解了该阶段的checker方法的实现:

Last updated

Was this helpful?