本文共 5147 字,大约阅读时间需要 17 分钟。
上一篇文章介绍了event模块的配置结构体的初始化以及模块中的一些初始化函数,比如init_process函数。这些工作都是在服务器启动之前必须完成的。
在介绍event模块的配置结构体初始化时,我们知道event模块的解析是从events{}
“events”关键字开始的。也就是说与event模块相关的所有配置项都必须在配置文件的events块中。
本篇博文将开始介绍http模块的初始化操作。相对event模块来说,http模块要复杂的多。无论是从功能,数量还是模块的配置结构体在conf_ctx中的组织上看,都相当复杂。
这篇博文开始遵循之前一篇对event模块的分析方式对http模块展开分析。为了直观分析,我们在最开始先给出HTTP模块在conf_ctx中的组织形式,当然,这种情况下给出的组织形式还是比较粗糙的,后面我们会跟踪http部分配置文件的解析代码,进一步细化这个配置模块的组织形式。
这个sector我们结合配置文件的形式看一下配置文件中各种形式的http{}块对应的模块配置结构体的组织形式:
http{ .... server{ ..... location { ..... } }}
如上的http配置模式应该是最普遍使用的形式了。很多其他配置形式都是往这个形式上添加server{}块或者location块变成的。
我们下面看一下针对这个http配置块形式的http配置结构体的组织形式:
如上图所示conf_ctx指向的是核心模块创建的配置结构体数组。注意我们这里不关心顺序,因为http模块对应的核心数组创建的配置结构体不一定是在conf_ctx的第二个位置。
从这个图中我们可以直观地看到,对应每一个http块,server块,location块都会创建一个
ngx_http_conf_ctx_t
的结构体。这个结构体具有如下形式:
typedef struct { void **main_conf; void **srv_conf; void **loc_conf;} ngx_http_conf_ctx_t;
其中每一个成员都指向一个指针数组。指针数组的长度为Nginx中所有HTTP模块的总数,而指针数组中的每个元素都指向一个配置结构体的指针。所以我们可以说:
Nginx为每个HTTP模块维护三个配置结构体,分别是:main_conf,srv_conf,loc_conf。可以说,每个HTTP模块的感兴趣配置项分为三种类型,这三种类型的配置项的值分别存到该模块对应的main_conf,srv_conf,loc_conf中。根据配置中http块的编写规则,我们知道,http块,server块和location块是有等级关系的。比如server块只能嵌套在http块里面,location块可以嵌套在server块和location块里,而http块始终只能处于最外层。
对应地,我们从图中可以看到。server块的
ngx_http_conf_ctx_t->main_conf
指向它上一层的http块对应的main_loc数组,而不会自己创建一个自己的main_conf。可以理解为,server块会继承它上一层的main_conf;对应的,location块会继承它上一层(server或者location块)的main_conf和srv_conf。这个想法也是很自然的。毕竟对于server块来说,他本来负责的就是server相关的配置项,至于http一层的main配置项,它继承它的上一级就可以了。location也是如此,它负责的只是server上这个location的配置信息,至于这个server的配置信息,它只需要继承它上一级就可以了。
同时这样还有一个好处,就是节省内存。想象以下这种配置:
http{ server{ #server2; .... } server{ #server2; .... } ...}
如果为每个server块都单独创建一个main_conf,会有以下几个问题:
对于location也有相似的原因。这里就不赘述了。
这里我们需要注意的是,NGINX是允许在location中嵌套location的。如下所示
http{ server{ location{ location{ location{ ........ } } } }}
它的直观图原理和之前是一样的。只不过多增加了几个location块的ngx_http_conf_ctx_t。
这么组织是很好的,这种组织形式很好地反映了http{}块的层级结构。但是如果单纯这样的话,还会存在一个问题:
比如现在Nginx在运行时想要找到某个server块的ngx_http_conf_ctx_t。单纯按照上面的组织形式,从conf_ctx开始是找不到的。同样,如果想找到server块中的某个location块的ngx_http_conf_ctx_t,也是找不到的。为了解决上面的问题,必须额外设计一些成员。
Nginx的对这个问题的解决负担落在HTTP模块
ngx_http_core_module
上。
这个模块是HTTP类型模块数组中位于第一个位置上的模块。如下图中红色部分代表的就是ngx_http_core_module模块对应的配置结构体的位置:
ngx_http_core_main_conf_tngx_http_core_srv_conf_tngx_http_core_loc_conf_t
如我们前面所说,server块只能嵌套在http块中,因此server块的组织信息按道理应该放在ngx_http_core_main_conf_t中,事实也确实是这样的:
typedef struct { ngx_array_t servers; /* ngx_http_core_srv_conf_t */ ngx_http_phase_engine_t phase_engine; ngx_hash_t headers_in_hash; ngx_hash_t variables_hash; ngx_array_t variables; /* ngx_http_variable_t */ ngx_uint_t ncaptures; ngx_uint_t server_names_hash_max_size; ngx_uint_t server_names_hash_bucket_size; ngx_uint_t variables_hash_max_size; ngx_uint_t variables_hash_bucket_size; ngx_hash_keys_arrays_t *variables_keys; ngx_array_t *ports; ngx_uint_t try_files; /* unsigned try_files:1 */ ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];} ngx_http_core_main_conf_t;
其中的第一个成员”servers”就是将http块中所有的server块组织起来的关键。不过需要讲一下的是:
servers数组中存的并不是指向server块的ngx_http_conf_ctx_t指针,而该server块中ngx_http_core_module创建的ngx_http_core_srv_conf_t 结构体。 而ngx_http_core_srv_conf_t 结构体中有一个指针typedef struct { ....... /* server ctx */ ngx_http_conf_ctx_t *ctx; .......} ngx_http_core_srv_conf_t;
这个ctx指针指向的是该ngx_http_core_srv_conf_t 所属的ngx_http_conf_ctx_t。所以说通过ngx_http_core_srv_conf_t 也就可以找到对应的ngx_http_conf_ctx_t了。
对于location的组织原理也是差不多。前面说过location块可以嵌套在location块和server块中。也就是说,我们只能选择ngx_http_core_loc_conf_t来组织location块。因为如果选择ngx_http_core_srv_conf_t的话,server块和嵌套在这个server块里面的location块将会用同一个ngx_http_core_srv_conf_t,这就不能实现location嵌套location了。因为要保证location可以嵌套location,我们必须用server块和location块不共享的结构体来做组织。所以只能选
ngx_http_core_loc_conf_t。确实,Nginx中ngx_http_core_loc_conf_t的定义中
struct ngx_http_core_loc_conf_s { ...... ngx_queue_t *locations; ...}
locations就是起组织作用的关键。他是一个队列。对于server块中的 locations,它组织的是server块中嵌套的所有location,不包括location中嵌套的location。而对于location块的locations,它组织的是location中嵌套的location。这样就实现了location嵌套location。
这篇博文简单介绍了Nginx中HTTP模块在cycle的conf_ctx中的组织形式。Nginx靠核心模块ngx_http_module将所有的HTTP模块组织在conf_ctx中。然后靠ngx_http_conf_ctx_t中的三个指针一方面存储配置结构体信息,一方面实现继承,从而实现层级的组织。我们从这里也发现,每个HTTP模块感兴趣的配置项可能有三种类型:main_conf,srv_conf,loc_conf. 换句话说,每个http模块将它感兴趣的关于http全局,每个server,每个location的配置信息分别存储。而不是放在一起存储,这样逻辑更清晰。
HTTP模块中的ngx_http_core_module也对所有配置模块的组织起到了关键作用。NGINX主要是靠这个模块将http嵌套下的所有server块,server块嵌套的所有location块和location块嵌套的所有location块组织了起来。实现了这种不断嵌套的形式。
这一章只是介绍了配置结构体的组织形式。后面还会更详细介绍HTTP模块的解析和功能。