您的位置:首页 > 理论基础 > 计算机网络

Nginx源码剖析--HTTP模块配置结构体在conf_ctx中的组织

2017-10-21 15:00 501 查看
前言

http模块配置结构体组织架构
问题

解决方案

总结

前言

上一篇文章介绍了event模块的配置结构体的初始化以及模块中的一些初始化函数,比如init_process函数。这些工作都是在服务器启动之前必须完成的。

在介绍event模块的配置结构体初始化时,我们知道event模块的解析是从

events{
}


“events”关键字开始的。也就是说与event模块相关的所有配置项都必须在配置文件的events块中。

本篇博文将开始介绍http模块的初始化操作。相对event模块来说,http模块要复杂的多。无论是从功能,数量还是模块的配置结构体在conf_ctx中的组织上看,都相当复杂。

这篇博文开始遵循之前一篇对event模块的分析方式对http模块展开分析。为了直观分析,我们在最开始先给出HTTP模块在conf_ctx中的组织形式,当然,这种情况下给出的组织形式还是比较粗糙的,后面我们会跟踪http部分配置文件的解析代码,进一步细化这个配置模块的组织形式。

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,会有以下几个问题:

创建多个main_conf数组很浪费空间

逻辑上来说,我们希望所有server的main_loc应该是一致的。所以后面我们需要对所有server和http块的main_conf进行merge,显然这个复杂度是很高的。

对于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_module,其实意思就是ngx_http_core_module模块的配置结构体中包含了一些结构管理信息:

ngx_http_core_module的配置结构体如下所示:

ngx_http_core_main_conf_t
ngx_http_core_srv_conf_t
ngx_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模块的解析和功能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: