2019独角兽企业重金招聘Python工程师标准>>>
1. 模块介绍
高度模块化的设计是nginx的架构基础。在nginx中,除了少量的核心代码,其他一切皆为模块。这种模块化设计同时具有以下几个特点:
- 高度抽象的模块接口
所有的模块都遵循着同样的 ngx_module_t 接口设计规范,这减少了整个系统中的变数。
- 模块接口非常简单,具有很高的灵活性
模块的基本接口 ngx_module_t 足够简单,只涉及模块的初始化、退出以及对配置项的处理、这同时也带来了足够的灵活性,使得nginx比较简单地实现了动态可修改性。
- 配置模块的设计
ngx_module_t接口有一个type成员,它指明了nginx允许在设计模块时定义模块类型这个概念,允许专注于不同领域的模块按照类型来区别。而配置类型模块是唯一一种只有1个模块的模块类型。配置模块的类型叫做NGX_CONF_MODULE,它仅有的模块叫做ngx_conf_module。这是nginx最底层的模块,它指导着所有模块以配置项为核心来提供功能。因此,它是其他所有模块的基础。配置模块使nginx提供了高可配置性,高可扩展性,高可定制性,高可伸缩性。
- 核心模块接口的简单化
nginx中定义了一种基础类型的模块——核心模块。核心模块的存在简化了nginx的设计,使得非模块化的框架代码只关注于如何调用这些核心模块。
- 多层次、多类别的模块设计
所有的模块间是分层次的,分类别的。不同的模块虽然都具备相同的ngx_module_t接口,单在请求处理流程中的层次并不相同。nginx将各功能组织成一条链,当有请求到达的时候,请求依次经过这条链上的部分或者全部模块进行处理。
2. 模块的分类
nginx模块分为核心模块和功能性模块,功能性模块包括conf、event、http、mail、stream几大类。除了conf类以外,其他几类功能性模块都包含众多的模块,并且每个类别中都有一个核心模块。其主要作用是提供本类别功能通用的框架接口,逻辑处理等,并调用同类型的其他模块完成具体的功能。例如NGX_HTTP_MODULE类别中的核心模块ngx_http_core_module,负责http块配置项中通用的配置解析、http请求处理的整体流程,并调用其他NGX_HTTP_MODULE模块完成具体的处理。
nginx中常用模块分层分类:
3. 模块的相关结构体
- ngx_module_t接口
struct ngx_module_s {
ngx_uint_t ctx_index; // 模块在同类型模块数组中的索引序号
ngx_uint_t index; // 模块在所有模块数组中的索引序号
char * name; // 模块的名称
ngx_uint_t spare0; // 保留变量
ngx_uint_t spare1; // 保留变量
ngx_uint_t version; // 模块的版本号 目前只有一种,默认为1
const char* signature;
void * ctx; // 模块上下文结构体 不同的模块指向不同的结构体
ngx_command_t * commands; // 模块关心的配置项表 配置文件解析时会查找该表
ngx_uint_t type; // 模块类型 如NGX_CORE_MODULE
ngx_int_t (*init_master)(ngx_log_t *log); // master进程启动时回调
ngx_int_t (*init_module)(ngx_cycle *cycle); // 初始化模块时回调
ngx_int_t (*init_process)(ngx_cycle_t *cycle); // worker进程启动时回调
ngx_int_t (*init_thread)(ngx_cycle_t *cycle); // 线程启动时回调(nginx暂时无多线程模式)
void (*exit_thread)(ngx_cycle_t *cycle); // 线程退出时回调
void (*exit_process)(ngx_cycle_t *cycle); // worker进程退出时回调
void (*exit_master)(ngx_cycle_t *cycle); // master进程退出时回调
uintptr_t spare_hook0; // 保留字段
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};
typedef struct ngx_module_s ngx_module_t;
- 配置项
struct ngx_command_s {
// 配置项的名称
ngx_str_t name;
// 配置项的类型
ngx_uint_t type;
// 配置项解析处理函数
char * (*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
// 用于指示配置项所处内存的相对偏移位置
ngx_uint_t conf;
// 表示当前配置项在整个存储配置项的结构体中的偏移位置
ngx_uint_t offset;
// 配置项读取后的处理方法 必须是ngx_conf_post_t结构体的指针
void * post;
};
typedef struct ngx_command_s ngx_command_t;
- 核心模块类型的上下文
typedef struct {
// 模块的名称 例如 core, http, mail
ngx_str_t name;
// 分配内存(创建相关结构体)用于存储配置项解析时配置项值的存储
void * (*create_conf)(ngx_cycle_t *cycle);
// 初始化配置项的初始值
char * (*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t ;
4. 模块初始化流程
- 编译生成ngx_modules.c
执行configure后,会在objs目录下生成nginx_modules.c源文件。这个源文件中有两个很重要的全局变量ngx_modules和ngx_module_names,前者保存了nginx将要使用的全部模块,后者则记录了这些模块的名称。
ngx_module_t *ngx_modules[] = {
&ngx_core_module,
&ngx_errlog_module,
&ngx_conf_module,
&ngx_openssl_module,
&ngx_regex_module,
&ngx_events_module,
&ngx_event_core_module,
...
};
char *ngx_module_names[] = {
"ngx_core_module"
"ngx_errlog_module",
"ngx_conf_module",
"ngx_openssl_module",
"ngx_regex_module",
"ngx_events_module",
"ngx_event_core_module",
...
};
- 预初始化
初始化每个模块的索引以及名称。
ngx_int_t ngx_preinit_modules(void)
{
ngx_uint_t i;
for( i = 0; ngx_modules[i]; i++ )
{
ngx_modules[i]->index = i;
ngx_modules[i]->name = ngx_module_names[i];
}
ngx_modules_n = i;
ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;
return NGX_OK;
}
- 针对核心模块创建配置解析时需要的上下文
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
...
for( i = 0; cycle->modules[i]; i++ ) {
if( cycle->modules[i]->type != NGX_CORE_MODULE ) {
continue;
}
module = cycle->modules[i]->ctx;
if(module->create_conf) {
rv = module->create_conf(cycle);
if( rv == NULL ) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[cycle->modules[i]->index] = rv;
}
}
...
}
- 查找模块的配置项表并调用对应处理函数完成配置文件解析
- 初始化核心模块的配置
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
...
for( i = 0; cycle->modules[i]; i++ ) {
if( cycle->modules[i]->type != NGX_CORE_MODULE ) {
continue;
}
module = cycle->modules[i]->ctx;
if(module->init_conf) {
if( module->init_conf(cycle, cycle->conf_ctx[cycle->modules[i]->index]) == NGX_CONF_ERROR ) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
}
}
...
}
- 初始化各个模块
ngx_int_t ngx_init_modules(ngx_cycle_t * cycle)
{
ngx_uint_t i;
// 目前只有三个模块该函数指针非空
// 分别是 event_core, http_v2, regex
for( i = 0; cycle->modules[i]; i++ ) {
if(cycle->modules[i]->init_module) {
if(cycle->modules[i]->init_module(cycle) != NGX_OK) {
return NGX_ERROR;
}
}
}
return NGX_OK;
}
- 调用模块的进程启动初始化回调函数
// init_master 回调函数当前没有使用 即每个模块都设置为NULL
// init_process 回调函数当前有event_core, http_userid_filter, thread_pool模块进行设置
for( i = 0 ; cycle->modules[i]; i++ ) {
if( cycle->modules[i]->init_process ) {
if( cycle->modules[i]->init_process(cycle) == NGX_ERROR ) {
exit(2);
}
}
}
到此模块初始化流程结束,master/worker进程开始各自的循环。
参考:
《深入理解nginx》
《nginx开发从入门到精通》