基础知识一——模块

先看一下nginx 的模块设计,官方nginx共有五大类型的模块:核心模块、配置模块、事件模块、HTTP模块、MAIL模块。

nginx启动锁定黑窗 windows_nginx启动锁定黑窗 windows

各个模块的实现如下图:

nginx启动锁定黑窗 windows_nginx启动锁定黑窗 windows_02

ngx_module_t结构体作为所有模块的通用接口。ngx_command_t类型的commands数组则指定了模块处理配置项的方法。ngx_module_t中的ctx成员是一个void*指针,它可以指向任何数据,这给模块提供了很大的灵活性,使得多层次、多类型的模块设计成为可能。


核心模块包括:ngx_core_module、ngx_mail_module、ngx_http_module、ngx_openssl_module、ngx_events_module、ngx_errlog_module。

它们的ctx上下文为:

typedef struct {  
    //核心模块名称
    ngx_str_t	name;
   //解析配置项前,Nginx框架会调用create_conf方法。
    void	*(*create_conf)(ngx_cycle_t *cycle);
    //解析配置项完成后,nginx框架会调用init_conf方法
    char	*(*init_conf)(ngx_cycle_t *cycle, void *conf);    
} ngx_core_module_t;



http模块的ctx上下文:

typedef struct {  
  //在解析http{...}内的配置项前回调
  ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);                           
  //在解析http{...}内的配置项后回调
 ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
 //创建用于存储HTTP全局配置项的结构体,该结构体的成员将保存直属于http{}块的配置项参数。它会在解析main配置项后回调。
 void *(*create_main_conf)(ngx_conf_t *cf); 
//解析完main配置项后回调 
char *(*init_main_conf)(ngx_conf_t *cf, void *conf); 
//创建用于存储可同时出现在mian、srv级别配置项的结构体,该结构体中的成员与server配置是相关联的。
 void *(*create_srv_conf)(ngx_conf_t *cf); 
//把出现在main级别中的配置项合并到srv级别配置项中。
 char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); 
//创建用于存储可同时出现在main、srv、loc级别配置项的结构体,该结构体中的成员与location配置是相关联的。
 void *(*create_loc_conf)(ngx_conf_t *cf);
 //把出现在main、srv级别的配置项值合并到loc级别的配置项中。 
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
 } ngx_http_module_t;



基础知识二——ngx_module[]数组

nginx是通过全局指针数组ngx_modules[]组织模块的,数组的每一个元素均为一个全局ngx_module_t对象的指针。

00052: ngx_module_t *ngx_modules[] = {  
00053:    &ngx_core_module,  
00054:    &ngx_errlog_module,  
00055:    &ngx_conf_module,  
00056:    &ngx_events_module,  
00057:    &ngx_event_core_module,  
00058:    &ngx_epoll_module,  
00059:    &ngx_http_module,  
00060:    &ngx_http_core_module,  
00061:    &ngx_http_log_module,  
00062:    &ngx_http_upstream_module,  
00063:    &ngx_http_static_module,  
00064:    &ngx_http_autoindex_module,  
00065:    &ngx_http_index_module,  
00066:    &ngx_http_auth_basic_module,  
00067:    &ngx_http_access_module,  
00068:    &ngx_http_limit_zone_module,  
00069:    &ngx_http_limit_req_module,  
00070:    &ngx_http_geo_module,  
00071:    &ngx_http_map_module,  
00072:    &ngx_http_split_clients_module,  
00073:    &ngx_http_referer_module,  
00074:    &ngx_http_rewrite_module,  
00075:    &ngx_http_proxy_module,  
00076:    &ngx_http_fastcgi_module,  
00077:    &ngx_http_uwsgi_module,  
00078:    &ngx_http_scgi_module,  
00079:    &ngx_http_memcached_module,  
00080:    &ngx_http_empty_gif_module,  
00081:    &ngx_http_browser_module,  
00082:    &ngx_http_upstream_ip_hash_module,  
00083:    &ngx_http_stub_status_module,  
00084:    &ngx_http_write_filter_module,  
00085:    &ngx_http_header_filter_module,  
00086:    &ngx_http_chunked_filter_module,  
00087:    &ngx_http_range_header_filter_module,  
00088:    &ngx_http_gzip_filter_module,  
00089:    &ngx_http_postpone_filter_module,  
00090:    &ngx_http_ssi_filter_module,  
00091:    &ngx_http_charset_filter_module,  
00092:    &ngx_http_userid_filter_module,  
00093:    &ngx_http_headers_filter_module,  
00094:    &ngx_http_copy_filter_module,  
00095:    &ngx_http_range_body_filter_module,  
00096:    &ngx_http_not_modified_filter_module,  
00097:    NULL  
00098: };  
00099:



nginx启动锁定黑窗 windows_配置项_03


nginx启动总流程

nginx启动锁定黑窗 windows_HTTP_04

core module create_conf

for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }
//得到core module
        module = ngx_modules[i]->ctx;
//如果create_conf存在,则直接创建config.
        if (module->create_conf) {
            rv = module->create_conf(cycle);
            if (rv == NULL) {
                ngx_destroy_pool(pool);
                return NULL;
            }
//保存config.
            cycle->conf_ctx[ngx_modules[i]->index] = rv;
        }
    }



此时的内存布局:

nginx启动锁定黑窗 windows_配置项_05

解析配置文件


当所有的core module的config都创建完毕后,就要开始解析配置文件了,解析配置文件它会一行行读取,然后如果遇到指令,则会查找到对应的ngx_command_t对象,然后执行对应的回调set方法。这里所有动作都在ngx_conf_parse这个函数中进行。


解析HTTP


当解析到http{}时,就回调ngx_http_block方法,这个方法包括了HTTP框架的完整初始化流程。




nginx启动锁定黑窗 windows_nginx启动锁定黑窗 windows_06



1)步源码:


ngx_http_max_module = 0;
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }
//每个模块都有自己对应的索引值.
        ngx_modules[m]->ctx_index = ngx_http_max_module++;
    }
2)-5)步源码:


ngx_http_conf_ctx_t         *ctx;
//开始初始化,可以看到默认会分配max个config
    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }
//下面省略了srv和loc的创建
.......................................
 
//开始遍历
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }
//得到对应的module上下文
        module = ngx_modules[m]->ctx;
//得到对应的索引
        mi = ngx_modules[m]->ctx_index;
//如果有对应的回调,则调用回调函数,然后将返回的模块config设置到ctx的对应的conf列表中。
        if (module->create_main_conf) {
            ctx->main_conf[mi] = module->create_main_conf(cf);
            if (ctx->main_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
        if (module->create_srv_conf) {
            ctx->srv_conf[mi] = module->create_srv_conf(cf);
            if (ctx->srv_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
//下面省略了loc的调用。
    }

相关结构:


typedef struct {
   /*指向一个指针数组,数组中的每个成员都是又所有HTTP模块的create_main_conf方法创建的存放全局配置项的结构体,他们存放着解析直属http{}块内的main级别的配置项参数。*/
   void        **main_conf;
    /*指向一个指针数组,数组中的每个成员都是又所有HTTP模块的create_srv_conf方法创建的与server相关的结构体,他们或存放main级别配置项,或存放srv级别配置项,这与当前的ngx_http_conf_ctx_t是在解析http{}或者server{}块时创建的有关。*/
   void        **srv_conf;
/*指向一个指针数组,数组中的每个成员都是又所有HTTP模块的create_loc_conf方法创建的与location相关的结构体,他们可能存放main、srv、loc级别的配置项,这与当前的ngx_http_conf_ctx_t是在解析http{}、server{}或location{}快时创建的有关。*/
   void        **loc_conf;


} ngx_http_conf_ctx_t;


6)步源码:


cf->ctx = ctx;
 
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }
 
        module = ngx_modules[m]->ctx;
//调用preconfiguration。
        if (module->preconfiguration) {
            if (module->preconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

此时内存布局:


7)步源码:


cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
rv = ngx_conf_parse(cf, NULL);

这一步会解析main级别文件:内存分析晚些整理上来。

8)-9)步源码:


for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }
//和上面类似,首先取得模块以及对应索引。
        module = ngx_modules[m]->ctx;
        mi = ngx_modules[m]->ctx_index;
 
        /* init http{} main_conf's */
//如果有init_main_conf,则首先初始化main conf.
        if (module->init_main_conf) {
            rv = module->init_main_conf(cf, ctx->main_conf[mi]);
            if (rv != NGX_CONF_OK) {
                goto failed;
            }
        }
//然后开始merge config。
        rv = ngx_http_merge_servers(cf, cmcf, module, mi);
        if (rv != NGX_CONF_OK) {
            goto failed;
        }
    }