nginx源码分析——模块

原创
2016/06/17 15:21
阅读数 478

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开发从入门到精通》

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
2 收藏
0
分享
返回顶部
顶部