29 Dec 2021

nginx源码解析之模块开发

首先看一下对应的结构化调用

Nginx模块非常之多,可以认为所有代码都是以模块的形式组织的,包括核心模块和功能模块,对于功能模块的选择,可以在进行configure主动指定,比如新增http_flv模块

./configure --with-http_flv_module
执行这个命令之后,生成的objs/ngx_modules.c源文件内就会包含对ngx_http_flv_module模块的引用

Nginx模块有很多,大体上可以分为4个类别
handlers:协同完成客户端请求的处理,产生响应数据,比如ngx_http_rewrite_module模块, 用于处理客户端请求的地址重写,ngx_http_static_module模块,负责处理客户端的静态页面请求,ngx_http_log_module模块,负责记录请求日志
filters:对于handlers产生的数据进行过滤处理,比如模块ngx_http_not_modified_filter_module
upstream:Nginx可以利用upstream模块充当反向代理的角色,对客户端发送的请求进行转发,比如ngx_http_proxy_module模块
load-balance:Nginx充当中间角色,由于后端真实服务器往往多于一个,比如ngx_http_upstream_ip_hash_module这样的load balance模块来实现不同的负载均衡算法

首先看一下模块的定义:

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;
    const char           *signature;

    //指向当前模块持有的数据,必须指向ngx_http_module_t接口
    void                 *ctx;
    
    //指向当前模块配置项解析数组
    ngx_command_t        *commands;
    
    //模块类型
    ngx_uint_t            type;

    //模块的各个回调函数
    ngx_int_t           (*init_master)(ngx_log_t *log);

    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
    void                (*exit_thread)(ngx_cycle_t *cycle);
    void                (*exit_process)(ngx_cycle_t *cycle);

    void                (*exit_master)(ngx_cycle_t *cycle);

    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;
};

以ngx_http_module_t模块为例,明显可以看到各个函数的回调时机
static ngx_http_module_t  ngx_http_core_module_ctx = {
    ngx_http_core_preconfiguration,        /* preconfiguration */
    ngx_http_core_postconfiguration,       /* postconfiguration */

    ngx_http_core_create_main_conf,        /* create main configuration */
    ngx_http_core_init_main_conf,          /* init main configuration */

    ngx_http_core_create_srv_conf,         /* create server configuration */
    ngx_http_core_merge_srv_conf,          /* merge server configuration */

    ngx_http_core_create_loc_conf,         /* create location configuration */
    ngx_http_core_merge_loc_conf           /* merge location configuration */
};

struct ngx_command_s {

    //配置项名称
    ngx_str_t             name;
    
    //配置项出现的位置 server{}/location{}
    ngx_uint_t            type;
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    ngx_uint_t            conf;
    ngx_uint_t            offset;
    void                 *post;
};

假如我们现在定义一个简单的http模块

#define ngx_command_s ngx_command_t
static ngx_command_t ngx_http_mytest_commands[] = {

    {
        ngx_string("mytest"),
        NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF,
        ngx_http_mytest,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL
    },
    ngx_null_command

}

ngx_http_mytest是ngx_command_t结构体中的set成员,当在某个配置项中出现了mytest配置项,Nginx将会调用ngx_http_mytest方法
static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {

    ngx_http_core_loc_conf_t *clcf;
    
    //找到配置项所属的配置块
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    
    //当匹配上mytest之后,ngx_http_mytest_handler将会处理和这个请求
    clcf->handler = ngx_http_mytest_handler;
    
    return NGX_CONF_OK;

}

ngx_http_mytest_handler其实就是对应在11个处理阶段中需要做的事,这个我们后面在讨论

最后定义mytest模块
#define ngx_module_s ngx_module_t;
ngx_module_t ngx_http_mytest_module = {

    NGX_MODULE_V1,
    &ngx_http_mytest_module_ctx,
    ngx_http_mytest_commands,
    NGX_HTTP_MODULE,
    NULL,
    ...
    

}

这样,mytest模块在编译时将会被加入到ngx_modules全局数组中,nginx在启动时,会调用所有模块的初始化回调方法

对模块做初始化

在nginx.c中的main函数
/* 初始化所有模块;并对所有模块进行编号处理 ngx_modules数却是在自动编译的时候生成的,位于objs/ngx_modules.c文件中 */
if (ngx_preinit_modules() != NGX_OK) {
    return 1;
}    


/**
 * 初始化所有模块;并对所有模块进行编号处理;
 */
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;
}

//cycle贯穿整个nginx生命周期
cycle = ngx_init_cycle(&init_cycle);

    //给模块分配内存
    if (ngx_cycle_modules(cycle) != NGX_OK) {
        ngx_destroy_pool(pool);
        return NULL;
    }
    
    //调用module的初始化方法,回调方法
    if (ngx_init_modules(cycle) != NGX_OK) {
        /* fatal */
        exit(1);
    }

//最终开始回调    
if (ngx_process == NGX_PROCESS_SINGLE) {
    ngx_single_process_cycle(cycle);

} else {
    ngx_master_process_cycle(cycle);
}    

Tags:
0 comments