8.2 Nginx的架构设计

8.1 节列出了进行Nginx设计时需要格外重视的7个关键点,本节将介绍Nginx是如何在这7个关键点上提升Nginx能力的。

8.2.1 优秀的模块化设计

高度模块化的设计是Nginx的架构基础。在Nginx中,除了少量的核心代码,其他一切皆为模块。这种模块化设计同时具有以下几个特点:

(1)高度抽象的模块接口

所有的模块都遵循着同样的ngx_module_t接口设计规范,这减少了整个系统中的变数,对于8.1节中列出的关键关注点,这种方式带来了良好的简单性、静态可扩展性、可重用性。

(2)模块接口非常简单,具有很高的灵活性

模块的基本接口ngx_module_t足够简单,只涉及模块的初始化、退出以及对配置项的处理,这同时也带来了足够的灵活性,使得Nginx比较简单地实现了动态可修改性(参见8.5节和8.6节,可知如何通过HUP信号在服务正常运行时使新的配置文件生效,以及通过USR2信号实现平滑升级),也就是保持服务正常运行下使系统功能发生改变。

如图8-1所示,ngx_module_t结构体作为所有模块的通用接口,它只定义了init_master、init_module、init_process、init_thread、exit_thread、exit_process、exit_master这7个回调方法(事实上,init_master、init_thread、exit_thread这3个方法目前都没有使用),它们负责模块的初始化和退出,同时它们的权限也非常高,可以处理系统的核心结构体ngx_cycle_t。在8.4节~8.6节中,可以看到以上7个回调方法何时会被调用。而ngx_command_t类型的commands数组则指定了模块处理配置项的方法(详见第4章)。

除了简单、基础的接口,ngx_module_t中的ctx成员还是一个void*指针,它可以指向任何数据,这给模块提供了很大的灵活性,使得下面将要介绍的多层次、多类型的模块设计成为可能。ctx成员一般用于表示在不同类型的模块中一种类型模块所具备的通用性接口。

8.2 Nginx的架构设计 - 图1

图 8-1 ngx_module_t接口及其对核心、事件、HTTP、mail等4类模块ctx上下文成员的具体化

(3)配置模块的设计

可以注意到,ngx_module_t接口有一个type成员,它指明了Nginx允许在设计模块时定义模块类型这个概念,允许专注于不同领域的模块按照类型来区别。而配置类型模块是唯一一种只有1个模块的模块类型。配置模块的类型叫做NGX_CONF_MODULE,它仅有的模块叫做ngx_conf_module,这是Nginx最底层的模块,它指导着所有模块以配置项为核心来提供功能。因此,它是其他所有模块的基础。配置模块使Nginx提供了高可配置性、高可扩展性、高可定制性、高可伸缩性。

(4)核心模块接口的简单化

Nginx还定义了一种基础类型的模块:核心模块,它的模块类型叫做NGX_CORE_MODULE。目前官方的核心类型模块中共有6个具体模块,分别是ngx_core_module、ngx_errlog_module、ngx_events_module、ngx_openssl_module、ngx_http_module、ngx_mail_module模块。为什么要定义核心模块呢?因为这样可以简化Nginx的设计,使得非模块化的框架代码只关注于如何调用6个核心模块(大部分Nginx模块都是非核心模块)。

核心模块的接口非常简单,如图8-1所示,它将ctx上下文进一步实例化为ngx_core_module_t结构体,代码如下。


typedef struct{

//核心模块名称

ngx_str_t name;

//解析配置项前,Nginx框架会调用create_conf方法

voidcreate_conf)(ngx_cycle_t*cycle);

//解析配置项完成后,Nginx框架会调用init_conf方法

charinit_conf)(ngx_cycle_tcycle,voidconf);

}ngx_core_module_t;


ngx_core_module_t上下文是以配置项的解析作为基础的,它提供了create_conf回调方法来创建存储配置项的数据结构,在读取nginx.conf配置文件时,会根据模块中的ngx_command_t把解析出的配置项存放在这个数据结构中;它还提供了init_conf回调方法,用于在解析完配置文件后,使用解析出的配置项初始化核心模块功能。除此以外,Nginx框架不会约束核心模块的接口、功能,这种简洁、灵活的设计为Nginx实现动态可配置性、动态可扩展性、动态可定制性带来了极大的便利,这样,在每个模块的功能实现中就会较少地考虑如何不停止服务、不重启服务来实现以上功能。

这种设计也使得每一个核心模块都可以自由地定义全新的模块类型。因此,作为核心模块,ngx_events_module定义了NGX_EVENT_MODULE模块类型,所有事件类型的模块都由ngx_events_module核心模块管理;ngx_http_module定义了NGX_HTTP_MODULE模块类型,所有HTTP类型的模块都由ngx_http_module核心模块管理;而ngx_mail_module定义了NGX_MAIL_MODULE模块类型,所有MAIL类型的模块则都由ngx_mail_module核心模块管理。

(5)多层次、多类别的模块设计

所有的模块间是分层次、分类别的,官方Nginx共有五大类型的模块:核心模块、配置模块、事件模块、HTTP模块、mail模块。虽然它们都具备相同的ngx_module_t接口,但在请求处理流程中的层次并不相同。就如同上面介绍过的核心模块一样,事件模块、HTTP模块、mail模块都会再次具体化ngx_module_t接口(由于配置类型的模块只拥有1个模块,所以没有具体化ctx上下文成员),如图8-2所示。

图8-2展示了Nginx常用模块间的关系。配置模块和核心模块这两种模块类型是由Nginx的框架代码所定义的,这里的配置模块是所有模块的基础,它实现了最基本的配置项解析功能(就是解析nginx.conf文件)。Nginx框架还会调用核心模块,但是其他3种模块都不会与框架产生直接关系。事件模块、HTTP模块、mail模块这3种模块的共性是:实际上它们在核心模块中各有1个模块作为自己的“代言人”,并在同类模块中有1个作为核心业务与管理功能的模块。例如,事件模块是由它的“代言人”——ngx_events_module核心模块定义,所有事件模块的加载操作不是由Nginx框架完成的,而是由ngx_event_core_module模块负责的。同样,HTTP模块是由它的“代言人”——ngx_http_module核心模块定义的,与事件模块不同的是,这个核心模块还会负责加载所有的HTTP模块,但业务的核心逻辑以及对于具体的请求该选用哪一个HTTP模块处理这样的工作,则是由ngx_http_core_module模块决定的。至于mail模块,因与HTTP模块基本相似,不再赘述。

8.2 Nginx的架构设计 - 图2

图 8-2 Nginx常用模块及其之间的关系

在这5种模块中,配置模块与核心模块都是与Nginx框架密切相关的,是其他模块的基础。而事件模块则是HTTP模块和mail模块的基础,原因参见8.2.2节。HTTP模块和mail模块的“地位”相似,它们都更关注于应用层面。在事件模块中,ngx_event_core_module事件模块是其他所有事件模块的基础;在HTTP模块中,ngx_http_core_module模块是其他所有HTTP模块的基础;在mail模块中,ngx_mail_core_module模块是其他所有mail模块的基础。