美文网首页
nginx 源代码分析 (二)

nginx 源代码分析 (二)

作者: RonZheng2010 | 来源:发表于2021-06-16 23:12 被阅读0次

1. nginx模块

nginx的功能分布在nginx模块中,一个模块为一个功能单元。每个nginx模块都专注于自己的功能点。Nginx将nginx模块组合起来,捕获和处理网络请求,写日志等。

表示nginx模块的结构是ngx_module_t。 全局数组ngx_modules[]保存nginx模块。通过在配置文件中指定模块名,也可以动态加载第三方模块。

ngx_module_t *ngx_modules[] = { 
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    &ngx_events_module,
    &ngx_event_core_module,
    &ngx_epoll_module,
    &ngx_http_module,
    .......
};

nginx模块划分成不同类型。

#define NGX_CORE_MODULE      0x45524F43
#define NGX_CONF_MODULE      0x464E4F43
#define NGX_EVENT_MODULE     0x544E5645
#define NGX_HTTP_MODULE      0x50545448

NGX_CORE_MODULE的例子如下:

  • ngx_core_module,处理全局设置,如进程/线程模式、CPU affinity、工作目录、动态加载用户模块等。
  • ngx_errlog_module,nginx的日志系统
  • ngx_events_modules,可以认为它是NGX_EVENT_MODULE类型模块的容器。
  • ngx_http_module,可以认为它是NGX_HTTP_MODULE类型模块的容器。

NGX_CONF_MODULE的例子如下:

  • ngx_conf_module,特意设计用来解析配置文件中的include指令

NGX_EVENT_MODULE的例子如下:

  • ngx_event_core_module,处理event模块的通用选项。
  • ngx_epoll_module,是event功能模块,基于epoll(),实现对socket的监听。

NGX_HTTP_MODULE的例子如下:

  • ngx_http_core_module,处理http模块的通用选项。
  • ngx_http_upstream_module,是http功能模块,将多个服务器定义成一个服务器组。

2. nginx配置数据结构

可以通过修改配置改变nginx模块的行为。nginx的配置数据保存在文件nginx.conf中。 解析nginx.conf的过程,同时也是加载nginx模块并配置它们的过程。如下是一个nginx.conf的例子。

daemon off;
master_process off;
worker_processes  1;

events {
    worker_connections  1024;
}

http {
  include mime.types;
  default_type application/octet-stream;
  keep_alive_timeout 65;

  server {
    listen 80;
    server_name localhost;

    location / {
      root html;
      index index.html index.htm;
    }
  }
}

nginx的配置是分层的。每组大括号 {} 内的内容为一层,大括号前的token为层的名字。

nginx模块可以创建自己的层。下图中是几个例子。

  • nginx_core_module创建顶层,相应地,创建的数据结构为ngx_core_conf_t,它的相关配置daemon、master_process、worker_processes保存在ngx_core_conf_t中;
  • nginx_events_module创建events层,相应地,创建数据结构为指针void,这个指针又指向一个void指针数组。
  • nginx_http_module创建http层,相应地,创建数据结构为ngx_http_conf_ctx_t,这个结构包含三个void*指针,每个指针又指向三个数组main_conf、srv_conf、和loc_conf。

nginx模块也可以创建多个层。

  • ngx_http_core_module创建了servers层和更深的locations层。相应地,创建了两层ngx_http_conf_ctx_t结构。

nginx也可以不创建自己的层,而直接使用其他模块创建的层。

  • 如ngx_event_core_module,它在ngx_events_module创建的层/结构下,创建自己的结构ngx_event_conf_t,保存它的配置,如worker_connections等。

3. nginx配置接口

nginx解析过程是一遍扫描完成的,遇到{}中的内容时“深度优先”,递归调用函数ngx_conf_parse()进行处理。

结构ngx_module_t保存nginx的模块信息。

  • 成员name为模块名,成员type是模块类型。

  • 成员index是该模块在模块数组中的索引。因为模块的配置数据数组与模块数组顺序完全一致,所以用index可以方便地得到它的配置数据。index是调用ngx_preinit_modules()设置的。

  • 成员ctx_index也是模块在模块数组中的索引。不同于index的是,它不是对所有类型的模块排序,而是只按类型排序。比如,所有NGX_EVENT_MODULE类型的模块按顺序算一个数组,所有NGX_HTTP_MODULE的模块按顺序算一个数组。这是因为在解析events{}层、http{}层时创建的数据结构,只针对该类型的模块,用ctx_index能方便得到这些数据结构。ctx_index是调用ngx_count_modules()设置的。

  • 成员函数init_master()、init_process()、init_thread()、exit_master()、exit_process()等定义一组接口,用于实现在“master模式初始化”、“进程/线程初始化时”,“master模式退出”等的操作。

  • 成员ctx指向一个特定于模块类型的结构,如NGX_CORE_MOUDLE类型的结构是ngx_core_module_t,NGX_EVENT_MODULE类型的结构是ngx_event_module_t,NGX_HTTP_MODULE类型的结构是ngx_http_module_t。 这些结构中都包括一组函数,用创建、初始化模块的配置数据。实际上这个结构就是在定义一个接口。为不同类型接口的模块定义不同接口的原因是,它们的初始化方式不同。

  • 在每一层{}处理时,nginx遍历ngx_cycle_t的成员modules[],为当前类型的模块,执行三个步骤:

    • 调用接口函数create_conf()创建配置数据结构,如果需要,做预处理preconfiguration(),
    • 调用ngx_conf_parse()进行“深度优先”的解析,
    • 初始化配置数据结构init_conf()/merge_conf(),如果需要,做后处理postconfiguration()。
  • 成员commands[]是一个ngx_command_t结构的数组。 ngx_command_t指定配置选项与处理函数的映射关系。成员name是选项名,type是选项类型,成员函数set()是处理函数。

    • 解析配置项时,根据它的名字调用处理函数。对于一个普通配置选项,set()设置这个选项。对于一个容器选项,如events、http、server、locations等,set()开启一个更深的层。

4. ngx_cycle_t

ngx_cycle_t是保存全局数据的地方。 main()的栈变量init_cycle是全局唯一的ngx_cycle_t变量。

ngx_cycle_t *init_cycle;
  • 成员modules[]保存所有的module。 全局数组ngx_modules[]中的模块会全部复制到这里,动态加载的模块创建后也保存在这里。modules_n是modules[]的大小,modules_n是实际的模块数量。
  • 成员pool是内存池,大小内存块都从这里分配。
  • 成员conf_ctx[] 保存模块的配置数据。
  • 成员connections保存网络连接,free_connections保存空闲的连接,listening保存用于监听的连接。
  • read_events和write_events分别用于保存读、写的事件ngx_event_t。

5. 准备模块阶段

5.1 ngx_preinit_modules()

ngx_preinit_modules()初始化全局数组ngx_modules[],主要是设置ngx_module的名称name和索引index。

  • 模块名来自全局数组ngx_module_names[]。
  • 将模块依次编号,编号保存在ngx_module的成员index中。后面init_cycle.conf_ctx[]数组中的配置数据也是一样的顺序,所以index可以用来得到模块的配置数据。
  • 设置全局变量ngx_modules_n和ngx_max_module的值。 ngx_modules_n是ngx_modules[]中的元素数量,ngx_max_module则还要加上动态加载的module的数量。

5.2 ngx_cycle_modules()

ngx_cycle_modules()初始化ngx_cycle_t的成员modules[]。

  • 调用ngx_palloc()分配长度为ngx_max_module + 1的ngx_module_t*数组。
  • 调用ngx_memcpy()从全局数组ngx_modules[]中复制所有ngx_module_t*指针。

5.3 main()

main()调用ngx_preinit_module()和ngx_init_cycle(),而ngx_init_cycle()又调用ngx_cycle_modules()。

6. 解析配置阶段

6.1 ngx_init_cycle()

ngx_init_cycle()直接解析配置文件的顶层部分。这里是解析每层的三个标准步骤。

第一步,遍历init_cycle.modules[]中的NGX_CORE_MODULE模块,调用ngx_core_module_t::create_conf(),创建配置数据。
第二步,调用ngx_conf_parse()解析配置文件。对于普通的选项(本层配置项,这里是顶层),直接设置,对于events{},http{},则递归调用其处理函数,深入到下一层的解析。
第三步,遍历init_cycle.modules[]中的NGX_CORE_MODULE模块,调用ngx_core_module_t::init_conf(),对配置数据中还没有设置的选项,进行初始化。

6.2 ngx_conf_parse()

ngx_conf_parse()被递归调用,用于解析配置文件。参数cf是ngx_conf_t,参数filename是文件名。

char* ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);

ngx_conf_t是配置数据上下文。

  • 配置文件打开后的信息ngx_conf_file_t,保存在成员conf_file中。
  • 成员module_type是当前层的模块类型
  • 成员cmd_type是当前解析的ngx_command_t类型。
  • 选项token解析后,保存在成员args数组中。

ngx_conf_parse()的步骤如下图。

  • 解析配置文件顶层部分时,filename是文件名;
    • 调用ngx_open_file()打开文件,文件的fd值保存在参数cf的成员conf_file中;
    • 分配NGX_CONF_BUFFER大小的ngx_buf_t,作为解析过程的读缓存。
  • 解析下一层(如evnts{}和http{}),递归调用ngx_conf_parse() 时,filename是NULL。

在循环中依次读取选项token并处理。

  • 调用ngx_conf_read_token()得到一个token。token的名称和值保存在参数ngx_conf_t的成员args[]中。
  • 调用ngx_conf_handler()处理这个token。

6.3 ngx_conf_read_token()

ngx_conf_read_token()从配置文件中读取一个token。

有两种选项token。

  • 一种是叶子token,包括关键值名称和值,以字符 ; 结尾。如下面的dameon和error_log。
  • 另一种是容器token,如下面的“events{}”。容器token中包括一组叶子token。
  • 字符 # 后面的部分是注释,解析时跳过。
daemon off;
error_log  stderr  debug;

events {
    worker_connections  1024;
}

ngx_conf_read_token() 在一个循环中进行如下操作。

  • 调用ngx_read_file(),从文件读取内容到缓存ngx_conf_t.buffer。其中调用pread()指定偏移位置读取。

  • 分析词法语法。

    • 调用ngx_pnalloc()分配空间。调用ngx_array_push(),将token的域依次ngx_conf_t.args[]。
    • 解析时,如果遇到字符 ; ,则args[]中应该是个叶子token,解析到;时就返回NGX_OK;如果遇到字符 { ,则args[]应该是个容器token,解析到 { 就返回NGX_CONF_BLOCK_START。ngx_conf_read_token()之后,token的处理函数负责跳过结尾的 } 。
      (这里如果有一个显式的状态机处理,会更好理解)
#define NGX_OK                 0
#define NGX_CONF_BLOCK_START  1 
#define NGX_CONF_BLOCK_DONE   2 
#define NGX_CONF_FILE_DONE     3 
  • 结果是args[]中保存了token的域。比如daemon行,arsgs[0]为”daemon”,args[1]为”off”;又如error_log行,args[0]为”error_log”,args[0]为”stderr”,args[0]为”debug”;events行,args[0]为”events”。

6.4 ngx_conf_handler()

ngx_conf_handler()处理读到的token。如前所述,token保存在参数ngx_conf_t 的成员args[]中。

  • 遍历所有module的所有command,找到与token的名称相匹配的ngx_command_t项。这里要作有效性检测,ngx_command_t所属的模块类型、ngx_command_t自身的类型都要与ngx_conf_t指定的相匹配。
  • 调用ngx_command_t的设置函数ngx_command_t::set()。根据ngx_command_t的类型分别处理,有三种情况:
    • NGX_DIRECT_CONF:配置数据放在ngx_conf_t.ctx[module.index]直接指向的结构中,如下图的ngx_core_conf_t的选项。
    • NGX_MAIN_CONF:配置数据放在ngx_conf_t.ctx[module.index]间接指向的结构中。
    • 以上类型都不指定。配置数据放在ngx_conf_t.ctx[module.ctx_index]间接指向的结构中。如下图中的1-2和2-2. 前者是NGX_EVENT_MODULE类型模块选项,后者是NGX_HTTP_MODULE类型模块的选项。

ngx_conf_handler关键部分的代码如下。

  • 前两种情况比较清楚。对于第三种情况,首先cmd->conf是在1-1,或2-1中的偏移,通过它找到下一级结构,如1-2,或2-2/2-3/2-4。然后ngx_module_t.ctx_index是在下一级结构中的偏移,通过它找到与该模块的配置数据结构。
  • 找到配置数据结构后,调用ngx_command_t::set(),设置选项。
if (cmd->type & NGX_DIRECT_CONF) {
conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index];
} 
else if (cmd->type & NGX_MAIN_CONF) {
  conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]);
} 
else if (cf->ctx) {
  confp = *(void **) ((char *) cf->ctx + cmd->conf);
  if (confp) {
    conf = confp[cf->cycle->modules[i]->ctx_index];
  }
}

rv = cmd->set(cf, cmd, conf);

第一种情况的例子是ngx_core_module模块的选项”daemon”。它指定了类型NGX_DIRECT_CONF,数据结构为ngx_core_conf_t。ngx_comand_t.offset是daemon选项在ngx_core_conf_t中的偏移,这里指定为offset(ngx_core_conf_t, daemon)。处理函数ngx_conf_set_flag_slot()按照这个偏移设置选项。

ngx_command_t  ngx_core_commands[] = {
    { ngx_string("daemon"),
      NGX_MAIN_CONF | NGX_DIRECT_CONF | NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      0,   
      offsetof(ngx_core_conf_t, daemon),
      NULL },
    ......
}

第二种情况的例子是ngx_events_module模块的选项”events”,它指定了类型NGX_MAIN_CONF。对应于图中1-1的地址,处理函数ngx_events_block()会创建下一级的结构,并保存在这里。

ngx_command_t  ngx_events_commands[] = {
    { ngx_string("events"),
      NGX_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_NOARGS,
      ngx_events_block,
      0,   
      0,   
      NULL },
};

第二种情况的另一个例子是ngx_http_module模块的选项”http”。同样指定类型NGX_MAIN_CONF。,对应于图中2-1的地址,处理函数ngx_http_block()会创建下一级的结构,并保存在这里。

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_event_core_module模块的worker_connections选项。这里要跳到图中1-2的位置。如前所说,ngx_events_module这时已经创建了这一层的结构。

  • ngx_command_t.conf是在1-1中的偏移,1-1只是一个void*指针,所以偏移为0。
ngx_command_t  ngx_event_core_commands[] = {
      { ngx_string("worker_connections"),
      NGX_EVENT_CONF | NGX_CONF_TAKE1,
      ngx_event_connections,               
      0,
      0,
      NULL },
}

第三种情况的另一个例子是ngx_http_core_module模块的variables_hash_max_size选项。

ngx_command_t  ngx_http_core_commands[] = {
    { ngx_string("variables_hash_max_size"),
      NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
      ngx_conf_set_num_slot,
      NGX_HTTP_MAIN_CONF_OFFSET,
      offsetof(ngx_http_core_main_conf_t, variables_hash_max_size),
      NULL },
}
  • 这里要跳到2-2的位置,对应于ngx_http_core_main_conf_t。ngx_http_module这时已经创建了这一层的结构。
  • 2-1是一个ngx_http_conf_ctx_t,包括三个指针,分别指向ngx_http_core_main_conf_t、ngx_http_core_srv_conf_t、ngx_http_core_loc_conf_t。
typedef struct {
    void  **main_conf;
    void  **srv_conf;
    void  **loc_conf;
} ngx_http_conf_ctx_t;
  • ngx_command_t.conf是在2-1中的偏移,在这里指定为NGX_HTTP_MAIN_CONF_OFFSET,所以指向ngx_http_core_main_conf_t。

6.5 ngx_command_t::set()

6.5.1 NGX_DIRECT_CONF类型

NGX_DIRECT_CONF类型主要是ngx_core_module模块的ngx_command_t。它的处理函数的例子是ngx_conf_set_flag_slot()。它从ngx_conf_t.args[]中解析选项值,并设置。

6.6 NGX_MAIN_CONF类型

NGX_MAIN_CONF类型主要是ngx_events_module模块和ngx_http_module模块的选项,处理函数的例子分别是ngx_event_block()和ngx_http_block()。

6.7 特殊情况 - include选项

ngx_conf_module的include选项是一种特殊情况,它是为在包括另一个配置文件准备的。如下面的例子。

include mime.types;

处理函数ngx_conf_include(),读取include指定的配置文件mime.types,原地展开并解析。

7. events{} 解析配置

“events{}”是ngx_events_module的选项,处理函数是ngx_events_block()。

  • 调用ngx_count_modules()得到NGX_EVENT_MODULE的个数。

    • ngx_count_modules()同时对所有的NGX_EVENT_MODULE类型模块编号,编号保存ngx_module_t.ctx_index。
  • 调用ngx_palloc()两次,给ngx_events_module分配配置数据结构,也就是图中的1-1、1-2部分。

  • 之后就是重复类似ngx_init_cycle()的三个标准步骤。

第一步,遍历所有NGX_EVENT_MODULE类型模块,ngx_event_module_t::create_conf()创建它的配置数据结构。比如,对于ngx_event_core_module,就是调用ngx_event_core_create_conf(),分配的数据结构是ngx_event_conf_t。

第二步,递归调用ngx_conf_parse()进行解析。

  • 注意这里暂时替换了ngx_conf_t结构。替换前,ngx_conf_t指向init_cycle.conf_ctx[],替换后,指向图中1-1的位置。所以ngx_conf_handler()处理events{}内部的叶子节点,就是代码中的第三种情况了。
    pcf = *cf; 
    cf->ctx = ctx; 
    cf->module_type = NGX_EVENT_MODULE;
    cf->cmd_type = NGX_EVENT_CONF;
    rv = ngx_conf_parse(cf, NULL);
    *cf = pcf; 
  • ngx_conf_parse()返回时,所有NGX_EVENT_MODULE类型的模块的配置数据应该都已经加载就绪。

第三步,遍历所有NGX_EVENT_MODULE类型的模块,ngx_event_module::init_conf()。如果有选项没有设置,则设置成缺省值。比如,对于ngx_event_core_module,就是调用ngx_event_core_init_conf()。

8. http{}解析配置

“http{}”是ngx_htttp_module模块的选项,处理函数是ngx_http_block()。

  • 调用ngx_count_modules()得到NGX_HTTP_MODULE类型模块的个数。

    • ngx_count_modules()同时对所有的NGX_HTTP_MODULE类型模块编号,编号保存ngx_module_t.ctx_index。
  • 调用ngx_palloc() 四次,给ngx_http_module分配配置数据结构,也就是图中的2-1、2-2、2-3、2-4部分。

  • 之后是重复类似ngx_init_cycle()的三个标准步骤。

    • 不同于ngx_events_module的是,http{}内部还有两层:server{}和更深的location{}。所以创建配置数据结构的有三个函数:ngx_http_module_t::create_main_conf()、ngx_http_module_t::create_srv_conf()、和ngx_http_module_t::create_loc_conf()。
http {
  include mime.types;
  default_type application/octet-stream;
  keep_alive_timeout 65;

  server {
    listen 80;
    servername localhost;

    location / {
      root html;
        index index.html index.htm;
    }
  }
}

第一步,遍历所有NGX_HTTP_MODULE类型模块,调用ngx_http_module_t::create_XXX_conf()创建它的配置数据结构。比如,对于ngx_http_core_module,就是调用ngx_http_core_create_XXX_conf(),分配的数据结构是ngx_http_core_XXX_conf_t。
遍历所有NGX_HTTP_MODULE类型模块,调用ngx_http_module_t::preconfiguration()预配置。比如,对于ngx_http_core_module,就是调用ngx_http_core_preconfiguration(),。

第二步,递归调用ngx_conf_parse()进行解析。

  • 注意这里暂时替换了ngx_conf_t结构。替换前,ngx_conf_t指向init_cycle.conf_ctx[],替换后,指向图中2-1的位置。所以ngx_conf_handler()处理http{}内部的叶子节点,就是代码中的第三种情况了。
    pcf = *cf; 
    cf->ctx = ctx; 
    cf->module_type = NGX_HTTP_MODULE;
    cf->cmd_type = NGX_HTTP_MAIN_CONF;
    rv = ngx_conf_parse(cf, NULL);
    *cf = pcf; 
  • ngx_conf_parse()返回时,所有NGX_HTTP_MODULE类型模块的NGX_HTTP_MAIN_CONF类型的配置数据,应该都已经加载就绪。

第三步,调用ngx_http_merge_servers(),其中遍历所有NGX_HTTP_MODULE类型的模块,调用ngx_http_module_t::merge_XXX_conf()。比如,对于ngx_http_core_module,就是调用ngx_http_core_merge_srv_conf()和ngx_http_core_merge_loc_conf()。

  • 遍历所有NGX_HTTP_MODULE类型模块,调用ngx_http_module_t::postconfiguration()后配置。比如,对于ngx_http_core_module,就是调用ngx_http_core_postconfiguration()。

9. server{}/location{} 解析配置

server{}节点是http{}的子节点,它绑定一组IP地址,代表一个服务器。Location{}节点又是server{}的子节点,它关联一个服务器上的路径。

在下面http地址中,server{}对应于192.168.1.10:8088部分,location{}对应于/server。

http://192.168.1.10:8088/service?cmd=xyz

server{}和location{}都由ngx_http_core_module模块处理。server{}的处理函数是ngx_http_core_server(),location{}的处理函数是ngx_http_core_location()。

如下图,红色框内是为server{}节点和location节点创建的配置数据结构。

红框内部分放大后,如下图所示。

9.1 ngx_http_core_server()

ngx_http_core_server()的步骤如下图。

  • 调用ngx_palloc() 三次,给ngx_http_core_module分配配置数据结构。调用三次而不是四次的原因是,ngx_http_conf_ctx_t.main_conf没有创建,而是指向了上一层,也就是http{}节点的main_conf。

然后是与ngx_init_cycle()中类似的标准三步骤。

第一步,遍历所有NGX_HTTP_MODULE类型模块,调用ngx_http_module_t::create_XXX_conf()创建它的配置数据结构。因为main_conf指向上一层,所以只调用ngx_http_module_t::create_srv_conf()和ngx_http_module_t::create_loc_conf()。

  • 将自身的ngx_http_core_srv_conf_t,关联到上一层(http{}层)的ngx_http_core_main_conf_t.servers。

第二步,递归调用ngx_conf_parse()进行解析。

  • 注意这里暂时替换了ngx_conf_t结构。替换前,ngx_conf_t指向http{}节点的ngx
    _http_conf_ctx_t,替换后,指向server{}节点的ngx_http_conf_t。所以ngx_conf_handler()处理server{}内部的叶子节点,就是代码中的第三种情况。
    pcf = *cf; 
    cf->ctx = ctx; 
    cf->cmd_type = NGX_HTTP_SRV_CONF;
    rv = ngx_conf_parse(cf, NULL);
    *cf = pcf; 
  • ngx_conf_parse()返回时,所有NGX_HTTP_MODULE类型模块的NGX_HTTP_SRV_CONF类型的配置数据,应该都已经加载就绪。特别地,监听端口应该已经由ngx_http_core_listen()读入。

第三步,这步与标准步骤不一样。不在这里调用ngx_http_module_t::merge_srv_conf()或ngx_http_module_t::merge_loc_conf(),实际上这是调用ngx_http_merge_servers()时完成的(调用ngx_http_block()处理http{}节点时)。

  • 调用ngx_http_add_listen(),加入监听端口。

9.2 ngx_http_core_listen()

如下的listen行指定了服务器的监听端口。

server {
listen 80;
            ...
}

ngx_http_core_module的ngx_command_t,指定了ngx_http_core_listen()负责读入listen行。

  • 调用ngx_parse_url() 解析端口字符串得到端口列表。
  • 遍历端口列表,调用ngx_http_add_listen(),保存端口信息。

9.3 ngx_http_add_listen()

ngx_http_add_listen()将指定端口打包,加入上一层(http{}层)的ngx_http_core_main_conf_t.ports[]。

  • 调用ngx_array_push()得到一个空闲的ngx_http_conf_port_t。
  • 调用ngx_http_add_address(),
    • 将参数ngx_http_listen_opt_t中的地址信息,加入ngx_http_conf_port_t.addrs[]
    • 将ngx_http_conf_addr_t.default_server设置为参数ngx_http_core_srv_conf_t。到这里,就把地址与ngx_http_core_srv_conf_t实例关联起来了。

9.4 ngx_http_core_location()

ngx_http_core_location()的步骤如下图。

  • 调用ngx_palloc() 两次,给ngx_http_core_module分配配置数据结构。调用两次而不是四次的原因是,ngx_http_conf_ctx_t.main_conf和ngx_http_conf_ctx_t.srv_conf没有创建,而是指向了上一层,也就是http{}节点的相应结构。

然后是与ngx_init_cycle()中类似的标准三步骤。

第一步,遍历所有NGX_HTTP_MODULE类型模块,调用ngx_http_module_t::create_loc_conf()创建它的配置数据结构。

  • 调用ngx_http_add_location(),加入服务器访问位置。

第二步,递归调用ngx_conf_parse()进行解析。

  • 注意这里暂时替换了ngx_conf_t结构。替换前,ngx_conf_t指向server{}节点的ngx
    _http_conf_ctx_t,替换后,指向location{}节点的ngx_http_conf_t。所以ngx_conf_handler()处理location{}内部的叶子节点,就是代码中的第三种情况。
    pcf = *cf; 
    cf->ctx = ctx; 
    cf->cmd_type = NGX_HTTP_LOC_CONF;
    rv = ngx_conf_parse(cf, NULL);
    *cf = pcf; 
  • ngx_conf_parse()返回时,所有NGX_HTTP_MODULE类型模块的NGX_HTTP_LOC_CONF类型的配置数据,应该都已经加载就绪。

第三步,这步与标准步骤不一样。这里没有函数调用。实际上这是调用ngx_http_merge_servers()时完成的(调用ngx_http_block()处理http{}节点时)。

9.5 ngx_http_merge_servers()

Ngx_http_merge_servers() 针对NGX_HTTP_MODULE类型的模块,调整server{}和location{}中的配置。遍历ngx_http_core_main_conf_t.servers[],对其中每个ngx_http_core_srv_conf_t实例,

  • 调用ngx_http_module_t::merge_srv_conf(),调整设置
  • 调用ngx_http_module_t::merge_loc_conf(),调整设置。
  • 调用ngx_http_merge_locations(),调整location{}的设置。

9.6 ngx_http_init_locations()

如前所述,ngx_http_block() 解析http{} 节点,在内部递归解析server{}和location{}节点。解析完成后,调用ngx_http_merge_servers(),合并server和location的相关节点。

  • 这里还要提一下的是,调用ngx_http_init_locations()和ngx_http_init_static_location_trees(),进一步优化location相关结构,比如创建location红黑树,以便提高访问速度。

相关文章

网友评论

      本文标题:nginx 源代码分析 (二)

      本文链接:https://www.haomeiwen.com/subject/ywfqyltx.html