模块管理是nginx非常有魅力的功能。
ngx_modules数组包含了所有注册使用的模块,该数组定义在objs/ngx_modules.c中:
55 ngx_module_t *ngx_modules[] = {
56 &ngx_core_module,
57 &ngx_errlog_module,
58 &ngx_conf_module,
59 &ngx_events_module,
nginx通过给模块设置类型来支持多级模块:
顶级模块的类型是NGX_CORE_MODULE;
如果某个模块支持子模块,给其子模块指定一种模块类型(如NGX_EVENT_MODULE和NGX_HTTP_MODULE),可以通过它的某个block指令来加载其子模块(如events和http);
初始化时,先初始化CORE模块,再递归加载CORE模块的子模块;
上级模块开辟一个context数组保存下级模块的context,通过这种方式构成一个模块森林;
ngx_cycle_t::conf_ctx是void *数组,保存着NGX_CORE_MODULE的context;
ngx_events_module的context也是一个void *数组,保存着events二级模块的context;
ngx_http_module是ngx_http_conf_ctx_t的指针,ngx_http_conf_ctx_t包含三个类型数组(main、srv和loc),保存着http二级模块的context;
nginx默认支持的模块有:
ngx_core_module
ngx_events_module
ngx_event_core_module
ngx_epoll_module
......
ngx_http_module
ngx_http_core_module
ngx_http_fastcgi_module
......
......
ngx_conf_module比较特殊,该模块不受模块类型限制,任何地方都有效;
其仅有一个include命令,实现类似c的include预处理命令功能,这样使得配置文件可以分文件管理;
模块的结构体如下所示:
102 struct ngx_module_s {
103 ngx_uint_t ctx_index; /* 父模块的所有子模块组成一个数组,为子模块开辟context数组:ctx_index表示当前模块在其父模块这个数组中的下标 */
104 ngx_uint_t index; /* 模块在ngx_modules数组中的下标 */
105
106 ngx_uint_t spare0;
107 ngx_uint_t spare1;
108 ngx_uint_t spare2;
109 ngx_uint_t spare3;
110
111 ngx_uint_t version;
112
113 void *ctx; /* 模块context;同一个级别的模块一般格式相同;譬如core模块、event子模块、http子模块等 */
114 ngx_command_t *commands; /* 模块支持的命令数组 */
115 ngx_uint_t type; /* 模块类型:NGX_CORE_MODULE, NGX_EVENT_MODULE, NGX_HTTP_MODULE等 */
116
117 ngx_int_t (*init_master)(ngx_log_t *log); /* 在master进程启动时调用 */
118
119 ngx_int_t (*init_module)(ngx_cycle_t *cycle); /* 在所有模块配置读取完毕后调用 */
120
121 ngx_int_t (*init_process)(ngx_cycle_t *cycle); /* 在进程启动时调用 */
122 ngx_int_t (*init_thread)(ngx_cycle_t *cycle); /* 在线程启动时调用 */
123 void (*exit_thread)(ngx_cycle_t *cycle); /* 在线程退出时调用 */
124 void (*exit_process)(ngx_cycle_t *cycle); /* 在进程退出时调用 */
125
126 void (*exit_master)(ngx_cycle_t *cycle); /* 在master进程退出时调用 */
127
128 uintptr_t spare_hook0;
129 uintptr_t spare_hook1;
130 uintptr_t spare_hook2;
131 uintptr_t spare_hook3;
132 uintptr_t spare_hook4;
133 uintptr_t spare_hook5;
134 uintptr_t spare_hook6;
135 uintptr_t spare_hook7;
136 };
core模块的context结构:
139 typedef struct {
140 ngx_str_t name; /* 模块名字 */
141 void *(*create_conf)(ngx_cycle_t *cycle); /* 创建模块config context */
142 char *(*init_conf)(ngx_cycle_t *cycle, void *conf); /* 读完配置后,初始化模块config context, conf即create_conf的返回值 */
143 } ngx_core_module_t;
event模块的context结构:
486 typedef struct {
487 ngx_str_t *name; /* name,create_conf,init_conf含义同core模块 */
488
489 void *(*create_conf)(ngx_cycle_t *cycle);
490 char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
491
492 ngx_event_actions_t actions; /* event回调函数集 */
493 } ngx_event_module_t;
http模块的context结构:
24 typedef struct {
25 ngx_int_t (*preconfiguration)(ngx_conf_t *cf); /* 在读http block下所有配置前调用 */
26 ngx_int_t (*postconfiguration)(ngx_conf_t *cf); /* 在读完配置且init config后调用 */
27
28 void *(*create_main_conf)(ngx_conf_t *cf); /* 创建main conf,全局一份 */
29 char *(*init_main_conf)(ngx_conf_t *cf, void *conf); /* 读完http block下所有配置后调用 */
30
31 void *(*create_srv_conf)(ngx_conf_t *cf); /* 创建server conf,全局一份,每个server一份 */
32 char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); /* 将全局属性merge到每个server,server继承自己没有配置但全局有配置的属性 */
33
34 void *(*create_loc_conf)(ngx_conf_t *cf); /* 创建location conf,全局一份,每个server一份,每个location一份 */
35 char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); /* 将全局、server属性merge到location中 */
36 } ngx_http_module_t;
http模块的启动分析
http指令类型是NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,回调函数是ngx_http_block;
http指令类型传达三个含义:
第一,ngx_http_module的config context是由其回调函数ngx_http_block创建的(设置了NGX_MAIN_CONF但没有设置NGX_DIRECT_CONF);
第二,该指令是一个block;
第三,该指令无参数;
118 static char *
119 ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
120 {
121 char *rv;
122 ngx_uint_t mi, m, s;
123 ngx_conf_t pcf;
124 ngx_http_module_t *module;
125 ngx_http_conf_ctx_t *ctx;
126 ngx_http_core_loc_conf_t *clcf;
127 ngx_http_core_srv_conf_t **cscfp;
128 ngx_http_core_main_conf_t *cmcf;
129
130 /* the main http context */
131
132 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); /* 创建ngx_http_module的config context */
133 if (ctx == NULL) {
134 return NGX_CONF_ERROR;
135 }
136
137 *(ngx_http_conf_ctx_t **) conf = ctx; /* 保存config context到conf->ctx_conf数组 */
138
139
140 /* count the number of the http modules and set up their indices */
141
142 ngx_http_max_module = 0;
143 for (m = 0; ngx_modules[m]; m++) {
144 if (ngx_modules[m]->type != NGX_HTTP_MODULE) { /* NGX_HTTP_MODULE是http子模块的模块类型 */
145 continue;
146 }
147
148 ngx_modules[m]->ctx_index = ngx_http_max_module++;
149 }
150
151
152 /* the http main_conf context, it is the same in the all http contexts */
153
154 ctx->main_conf = ngx_pcalloc(cf->pool,
155 sizeof(void *) * ngx_http_max_module);
156 if (ctx->main_conf == NULL) {
157 return NGX_CONF_ERROR;
158 }
159
160
161 /*
162 * the http null srv_conf context, it is used to merge
163 * the server{}s' srv_conf's
164 */
165
166 ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
167 if (ctx->srv_conf == NULL) {
168 return NGX_CONF_ERROR;
169 }
170
171
172 /*
173 * the http null loc_conf context, it is used to merge
174 * the server{}s' loc_conf's
175 */
176
177 ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
178 if (ctx->loc_conf == NULL) {
179 return NGX_CONF_ERROR;
180 }
181
182
183 /*
184 * create the main_conf's, the null srv_conf's, and the null loc_conf's
185 * of the all http modules
186 */
187
188 for (m = 0; ngx_modules[m]; m++) {
189 if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
190 continue;
191 }
192
193 module = ngx_modules[m]->ctx;
194 mi = ngx_modules[m]->ctx_index;
195
196 if (module->create_main_conf) {
197 ctx->main_conf[mi] = module->create_main_conf(cf);
198 if (ctx->main_conf[mi] == NULL) {
199 return NGX_CONF_ERROR;
200 }
201 }
202
203 if (module->create_srv_conf) {
204 ctx->srv_conf[mi] = module->create_srv_conf(cf);
205 if (ctx->srv_conf[mi] == NULL) {
206 return NGX_CONF_ERROR;
207 }
208 }
209
210 if (module->create_loc_conf) {
211 ctx->loc_conf[mi] = module->create_loc_conf(cf);
212 if (ctx->loc_conf[mi] == NULL) {
213 return NGX_CONF_ERROR;
214 }
215 }
216 }
217
218 pcf = *cf; /* 保存上级block的context现场,以便在http block解析完毕后,恢复上级block的现场 */
219 cf->ctx = ctx; /* 更新context数组,为加载http子模块命令做准备 */
220
221 for (m = 0; ngx_modules[m]; m++) {
222 if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
223 continue;
224 }
225
226 module = ngx_modules[m]->ctx;
227
228 if (module->preconfiguration) {
229 if (module->preconfiguration(cf) != NGX_OK) { /* 加载配置前,为http子模块调用preconfiguration回调 */
230 return NGX_CONF_ERROR;
231 }
232 }
233 }
234
235 /* parse inside the http{} block */
236
237 cf->module_type = NGX_HTTP_MODULE; /* 此处设置模块类型为NGX_HTTP_MODULE */
238 cf->cmd_type = NGX_HTTP_MAIN_CONF; /* 此处设置命令类型为NGX_HTTP_MAIN_CONF,和上一句结合起来,即仅加载http子模块中NGX_HTTP_MAIN_CONF类型的命令 */
239 rv = ngx_conf_parse(cf, NULL); /* parse http block */
240
241 if (rv != NGX_CONF_OK) {
242 goto failed;
243 }
244
245 /*
246 * init http{} main_conf's, merge the server{}s' srv_conf's
247 * and its location{}s' loc_conf's
248 */
249
250 cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
251 cscfp = cmcf->servers.elts;
252
253 for (m = 0; ngx_modules[m]; m++) {
254 if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
255 continue;
256 }
257
258 module = ngx_modules[m]->ctx;
259 mi = ngx_modules[m]->ctx_index;
260
261 /* init http{} main_conf's */
262
263 if (module->init_main_conf) {
264 rv = module->init_main_conf(cf, ctx->main_conf[mi]);
265 if (rv != NGX_CONF_OK) {
266 goto failed;
267 }
268 }
269
270 rv = ngx_http_merge_servers(cf, cmcf, module, mi); /* merge server和location配置,实现server、location配置继承功能 */
271 if (rv != NGX_CONF_OK) {
272 goto failed;
273 }
274 }
275
276
277 /* create location trees */
278
279 for (s = 0; s < cmcf->servers.nelts; s++) {
280
281 clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
282
283 if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) { /* 将locations排序、分类:1、exact_match、noregex和普通location按字母序存放在ngx_http_core_loc_conf_t::locations队列中(当路径相同时,excact_match排在前面);2、regex location按定义顺序存放在ngx_http_core_loc_conf_t::regex_locations数组中;3、named location按字母序存放在ngx_http_core_srv_conf_t::named_locations中;4、noname locations被丢弃; */
284 return NGX_CONF_ERROR;
285 }
286
287 if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) { /* 将ngx_http_core_loc_conf_t::locations中保存的路径转变成一个高效的前缀匹配字典 */
288 return NGX_CONF_ERROR;
289 }
290 }
291
292
293 if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { /* 初始化phases */
294 return NGX_CONF_ERROR;
295 }
296
297 if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { /* 为http headers构建hash表 */
298 return NGX_CONF_ERROR;
299 }
300
301
302 for (m = 0; ngx_modules[m]; m++) {
303 if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
304 continue;
305 }
306
307 module = ngx_modules[m]->ctx;
308
309 if (module->postconfiguration) {
310 if (module->postconfiguration(cf) != NGX_OK) { /* 配置加载完毕后,为http子模块调用postconfiguration回调,注册phase handlers */
311 return NGX_CONF_ERROR;
312 }
313 }
314 }
315
316 if (ngx_http_variables_init_vars(cf) != NGX_OK) {
317 return NGX_CONF_ERROR;
318 }
319
320 /*
321 * http{}'s cf->ctx was needed while the configuration merging
322 * and in postconfiguration process
323 */
324
325 *cf = pcf; /* 恢复上级block的parse现场 */
326
327
328 if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { /* 根据注册的phase handlers初始化phase_engine */
329 return NGX_CONF_ERROR;
330 }
331
332
333 /* optimize the lists of ports, addresses and server names */
334
335 if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
336 return NGX_CONF_ERROR;
337 }
338
339 return NGX_CONF_OK;
340
341 failed:
342
343 *cf = pcf;
344
345 return rv;
346 }
在parse server和location block时,因为它们有自己的context,所以也需要保存和恢复上级parse现场;