概述
在上一篇文章《 Nginx 啟動初始化過程》簡單介紹了 Nginx 啟動的過程,並分析了其啟動過程的源碼。在啟動過程中有一個步驟非常重要,就是呼叫函數 ngx_init_cycle(),而該函數的呼叫為組態解析提供了介面。設定解析介面大概可分為兩個階段:準備資料階段與設定解析階段;
準備資料階段包括:
/* 配置文件解析 */ if (ngx_conf_param(&conf) != NGX_CONF_OK) {/* 带有命令行参数'-g' 加入的配置 */ environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {/* 解析配置文件*/ environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; }
ngx_conf_t 結構體 此結構體用於Nginx 在解析配置檔案時描述每個指令的一個資料結構,定義於檔案:src/core/ngx_conf_file.h
/* 解析配置时所使用的结构体 */ struct ngx_conf_s { char *name; /* 当前解析到的指令 */ ngx_array_t *args; /* 当前指令所包含的所有参数 */ ngx_cycle_t *cycle; /* 待解析的全局变量ngx_cycle_t */ ngx_pool_t *pool; /* 内存池 */ ngx_pool_t *temp_pool;/* 临时内存池,分配一些临时数组或变量 */ ngx_conf_file_t *conf_file;/* 待解析的配置文件 */ ngx_log_t *log; /* 日志信息 */ void *ctx; /* 描述指令的上下文 */ ngx_uint_t module_type;/* 当前解析的指令的模块类型 */ ngx_uint_t cmd_type; /* 当前解析的指令的指令类型 */ ngx_conf_handler_pt handler; /* 模块自定义的handler,即指令自定义的处理函数 */ char *handler_conf;/* 自定义处理函数需要的相关配置 */ };
ngx_conf_file_t 結構體的定義如下:
typedef struct { ngx_file_t file; /* 文件的属性 */ ngx_buf_t *buffer; /* 文件的内容 */ ngx_uint_t line; /* 文件的行数 */ } ngx_conf_file_t;
塊以及 upsteam 塊和mail 塊等。每一個這樣的配置塊代表一個作用域。高一級配置區塊的作用域包含了多個低一級配置區塊的作用域,也就是有作用域嵌套的現象。這樣,設定檔中的許多指令都會同時包含在多個作用域內。例如,http 區塊中的指令都可能同時處於http 區塊、server 區塊和location 區塊等三層作用域內。 在 Nginx 程式解析設定檔時,每個指令都應該記錄自己所屬的作用域範圍,而設定檔上下文ctx 變數就是用來存放目前指令所屬的作用域的。在Nginx 設定檔的各種設定區塊中,
http區塊可以包含子配置區塊,這在儲存結構上比較複雜。 指令類型type Nginx 程式中的不同的指令類型以巨集的形式定義在不同的來源碼頭類型定義在檔案: ngx_conf_file.h
#define NGX_DIRECT_CONF 0x00010000 #define NGX_MAIN_CONF 0x01000000 #define NGX_ANY_CONF 0x0F000000
類別指令在 Nginx 程式進入配置解析函數之前已經初始化完成,所以在進入配置解析函數之後可以將它們直接解析並儲存到實際的資料結構中,從設定檔的結構上來看,它們一般指的就是那些遊離於組態區塊之外、處於設定檔全域區塊部分的指令。 NGX_MAIN_CONF 類別指令包括event、
http、mail、upstream 等可以形成配置區塊的指令,它們沒有自己的初始化函數。 Nginx 程式在解析設定檔時如果遇到 NGX_MAIN_CONF 類別指令,將轉入對下一層指令的解析。 以下是 event 型別模組支援的指令類型。#define NGX_EVENT_CONF 0x02000000
#define NGX_HTTP_MAIN_CONF 0x02000000 #define NGX_HTTP_SRV_CONF 0x04000000 #define NGX_HTTP_LOC_CONF 0x08000000 #define NGX_HTTP_UPS_CONF 0x10000000 #define NGX_HTTP_SIF_CONF 0x20000000 #define NGX_HTTP_LIF_CONF 0x40000000 #define NGX_HTTP_LMT_CONF 0x80000000
src/core/ngx_conf_file.c 中實作。模組提供的介面函數主要是ngx_conf_parse
。另外,模組提供另一個單獨的介面ngx_conf_param,用來解析命令列傳遞的配置,這個介面也是對ngx_conf_parse 的包裝。首先看下設定解析函數 ngx_conf_parse
,其定義如下:/* * 函数功能:配置文件解析; * 支持三种不同的解析类型: * 1、解析配置文件; * 2、解析block块设置; * 3、解析命令行配置; */ char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) { char *rv; ngx_fd_t fd; ngx_int_t rc; ngx_buf_t buf; ngx_conf_file_t *prev, conf_file; enum { parse_file = 0, parse_block, parse_param } type; #if (NGX_SUPPRESS_WARN) fd = NGX_INVALID_FILE; prev = NULL; #endif if (filename) {/* 若解析的是配置文件 */ /* open configuration file */ /* 打开配置文件 */ fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); if (fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, ngx_open_file_n " \"%s\" failed", filename->data); return NGX_CONF_ERROR; } prev = cf->conf_file; cf->conf_file = &conf_file; if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", filename->data); } cf->conf_file->buffer = &buf; buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); if (buf.start == NULL) { goto failed; } buf.pos = buf.start; buf.last = buf.start; buf.end = buf.last + NGX_CONF_BUFFER; buf.temporary = 1; /* 复制文件属性及文件内容 */ cf->conf_file->file.fd = fd; cf->conf_file->file.name.len = filename->len; cf->conf_file->file.name.data = filename->data; cf->conf_file->file.offset = 0; cf->conf_file->file.log = cf->log; cf->conf_file->line = 1; type = parse_file; /* 解析的类型是配置文件 */ } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) { type = parse_block; /* 解析的类型是block块 */ } else { type = parse_param; /* 解析的类型是命令行配置 */ } for ( ;; ) { /* 语法分析函数 */ rc = ngx_conf_read_token(cf); /* * ngx_conf_read_token() may return * * NGX_ERROR there is error * NGX_OK the token terminated by ";" was found * NGX_CONF_BLOCK_START the token terminated by "{" was found * NGX_CONF_BLOCK_DONE the "}" was found * NGX_CONF_FILE_DONE the configuration file is done */ if (rc == NGX_ERROR) { goto done; } /* 解析block块设置 */ if (rc == NGX_CONF_BLOCK_DONE) { if (type != parse_block) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); goto failed; } goto done; } /* 解析配置文件 */ if (rc == NGX_CONF_FILE_DONE) { if (type == parse_block) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, expecting \"}\""); goto failed; } goto done; } if (rc == NGX_CONF_BLOCK_START) { if (type == parse_param) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "block directives are not supported " "in -g option"); goto failed; } } /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */ /* 自定义指令处理函数 */ if (cf->handler) { /* * the custom handler, i.e., that is used in the http's * "types { ... }" directive */ if (rc == NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\""); goto failed; } /* 命令行配置处理函数 */ rv = (*cf->handler)(cf, NULL, cf->handler_conf); if (rv == NGX_CONF_OK) { continue; } if (rv == NGX_CONF_ERROR) { goto failed; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv); goto failed; } /* 若自定义指令处理函数handler为NULL,则调用Nginx内建的指令解析机制 */ rc = ngx_conf_handler(cf, rc); if (rc == NGX_ERROR) { goto failed; } } failed: rc = NGX_ERROR; done: if (filename) {/* 若是配置文件 */ if (cf->conf_file->buffer->start) { ngx_free(cf->conf_file->buffer->start); } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, ngx_close_file_n " %s failed", filename->data); return NGX_CONF_ERROR; } cf->conf_file = prev; } if (rc == NGX_ERROR) { return NGX_CONF_ERROR; } return NGX_CONF_OK; }
函數完成。指令解析有兩種方式:一種是Nginx 內建的指令解析機制;另一種是自訂的指令解析機制。自訂指令解析原始碼如下所示:
/* 自定义指令处理函数 */ if (cf->handler) { /* * the custom handler, i.e., that is used in the http's * "types { ... }" directive */ if (rc == NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\""); goto failed; } /* 命令行配置处理函数 */ rv = (*cf->handler)(cf, NULL, cf->handler_conf); if (rv == NGX_CONF_OK) { continue; } if (rv == NGX_CONF_ERROR) { goto failed; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv); goto failed; }
/* Nginx内建的指令解析机制 */ static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) { char *rv; void *conf, **confp; ngx_uint_t i, found; ngx_str_t *name; ngx_command_t *cmd; name = cf->args->elts; found = 0; for (i = 0; ngx_modules[i]; i++) { cmd = ngx_modules[i]->commands; if (cmd == NULL) { continue; } for ( /* void */ ; cmd->name.len; cmd++) { if (name->len != cmd->name.len) { continue; } if (ngx_strcmp(name->data, cmd->name.data) != 0) { continue; } found = 1; /* * 只处理模块类型为NGX_CONF_MODULE 或是当前正在处理的模块类型; */ if (ngx_modules[i]->type != NGX_CONF_MODULE && ngx_modules[i]->type != cf->module_type) { continue; } /* is the directive's location right ? */ if (!(cmd->type & cf->cmd_type)) { continue; } /* 非block块指令必须以";"分号结尾,否则出错返回 */ if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" is not terminated by \";\"", name->data); return NGX_ERROR; } /* block块指令必须后接"{"大括号,否则出粗返回 */ if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" has no opening \"{\"", name->data); return NGX_ERROR; } /* is the directive's argument count right ? */ /* 验证指令参数个数是否正确 */ if (!(cmd->type & NGX_CONF_ANY)) { /* 指令携带的参数只能是 1 个,且其参数值只能是 on 或 off */ if (cmd->type & NGX_CONF_FLAG) { if (cf->args->nelts != 2) { goto invalid; } } else if (cmd->type & NGX_CONF_1MORE) {/* 指令携带的参数必须超过 1 个 */ if (cf->args->nelts < 2) { goto invalid; } } else if (cmd->type & NGX_CONF_2MORE) {/* 指令携带的参数必须超过 2 个 */ if (cf->args->nelts < 3) { goto invalid; } } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) { goto invalid; } else if (!(cmd->type & argument_number[cf->args->nelts - 1])) { goto invalid; } } /* set up the directive's configuration context */ conf = NULL; if (cmd->type & NGX_DIRECT_CONF) {/* 在core模块使用 */ conf = ((void **) cf->ctx)[ngx_modules[i]->index]; } else if (cmd->type & NGX_MAIN_CONF) {/* 指令配置项出现在全局配置中,不属于任何{}配置块 */ conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); } else if (cf->ctx) {/* 除了core模块,其他模块都是用该项 */ confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[ngx_modules[i]->ctx_index]; } } /* 执行指令解析回调函数 */ rv = cmd->set(cf, cmd, conf); if (rv == NGX_CONF_OK) { return NGX_OK; } if (rv == NGX_CONF_ERROR) { return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive %s", name->data, rv); return NGX_ERROR; } } if (found) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive is not allowed here", name->data); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unknown directive \"%s\"", name->data); return NGX_ERROR; invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of arguments in \"%s\" directive", name->data); return NGX_ERROR; }
struct ngx_command_s { /* 配置项名称 */ ngx_str_t name; /* 配置项类型,type将指定配置项可以出现的位置以及携带参数的个数 */ ngx_uint_t type; /* 处理配置项的参数 */ char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /* 在配置文件中的偏移量,conf与offset配合使用 */ ngx_uint_t conf; ngx_uint_t offset; /* 配置项读取后的处理方法,必须指向ngx_conf_post_t 结构 */ void *post; };
若在上面的通用配置解析中,定义了如下的 http 配置项结构,则回调用http 配置项,并对该http 配置项进行解析。此时,解析的是http block 块设置。
static ngx_command_t ngx_http_commands[] = { { ngx_string("http"), NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_http_block, 0, 0, NULL }, ngx_null_command };
http 是作为一个 core 模块被 nginx 通用解析过程解析的,其核心就是http{} 块指令回调,它完成了http 解析的整个功能,从初始化到计算配置结果。http{} 块指令的流程是:
创建并初始化上下文结构
当 Nginx 检查到 http{…} 配置项时,HTTP 配置模型就会启动,则会建立一个ngx_http_conf_ctx_t 结构,该结构定义在文件中:src/http/ngx_http_config.h
typedef struct{ /* 指针数组,数组中的每个元素指向所有 HTTP 模块 create_main_conf 方法产生的结构体 */ void **main_conf; /* 指针数组,数组中的每个元素指向所有 HTTP 模块 create_srv_conf 方法产生的结构体 */ void **srv_conf; /* 指针数组,数组中的每个元素指向所有 HTTP 模块 create_loc_conf 方法产生的结构体 */ void **loc_conf; }ngx_http_conf_ctx_t;
此时,HTTP 框架为所有 HTTP 模块建立 3 个数组,分别存放所有 HTTP 模块的create_main_conf、create_srv_conf 、create_loc_conf 方法返回的地址指针。ngx_http_conf_ctx_t 结构的三个成员分别指向这 3 个数组。例如下面的例子是设置 create_main_conf、create_srv_conf 、create_loc_conf 返回的地址。
ngx_http_conf_ctx *ctx; /* HTTP 框架生成 1 个 ngx_http_conf_ctx_t 结构变量 */ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); *(ngx_http_conf_ctx_t **) conf = ctx; ... /* 分别生成 3 个数组存储所有的 HTTP 模块的 create_main_conf、create_srv_conf、create_loc_conf 方法返回的地址 */ ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); /* 遍历所有 HTTP 模块 */ 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; /* 若实现了create_main_conf 方法,则调用该方法,并把返回的地址存储到 main_conf 中 */ if (module->create_main_conf) { ctx->main_conf[mi] = module->create_main_conf(cf); } /* 若实现了create_srv_conf 方法,则调用该方法,并把返回的地址存储到 srv_conf 中 */ if (module->create_srv_conf) { ctx->srv_conf[mi] = module->create_srv_conf(cf); } /* 若实现了create_loc_conf 方法,则调用该方法,并把返回的地址存储到 loc_conf 中 */ if (module->create_loc_conf) { ctx->loc_conf[mi] = module->create_loc_conf(cf); } } pcf = *cf; cf->ctx = ctx; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; if (module->preconfiguration) { if (module->preconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } }
调用通用模块配置解析流程解析
从源码 src/http/ngx_http.c 中可以看到,http 块的配置解析是调用通用模块的配置解析函数,其实现如下:
/* 调用通用模块配置解析 */ /* parse inside the http{} block */ cf->module_type = NGX_HTTP_MODULE; cf->cmd_type = NGX_HTTP_MAIN_CONF; rv = ngx_conf_parse(cf, NULL); if (rv != NGX_CONF_OK) { goto failed; }
根据解析结果进行配置项合并处理
/* 根据解析结构进行合并处理 */ /* * init http{} main_conf's, merge the server{}s' srv_conf's * and its location{}s' loc_conf's */ cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; cscfp = cmcf->servers.elts; 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 */ if (module->init_main_conf) { rv = module->init_main_conf(cf, ctx->main_conf[mi]); if (rv != NGX_CONF_OK) { goto failed; } } rv = ngx_http_merge_servers(cf, cmcf, module, mi); if (rv != NGX_CONF_OK) { goto failed; } } /* create location trees */ for (s = 0; s < cmcf->servers.nelts; s++) { clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) { return NGX_CONF_ERROR; } } if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; if (module->postconfiguration) { if (module->postconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } } if (ngx_http_variables_init_vars(cf) != NGX_OK) { return NGX_CONF_ERROR; } /* * http{}'s cf->ctx was needed while the configuration merging * and in postconfiguration process */ *cf = pcf; if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } /* optimize the lists of ports, addresses and server names */ if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) { return NGX_CONF_ERROR; } return NGX_CONF_OK; failed: *cf = pcf; return rv;
HTTP 配置解析流程
从上面的分析中可以总结出 HTTP 配置解析的流程如下:
合并配置项
HTTP 框架解析完毕 http{} 块配置项时,会根据解析的结果进行合并配置项操作,即合并 http{}、server{}、location{} 不同块下各HTTP 模块生成的存放配置项的结构体。其合并过程如下所示:
以下是合并配置项操作的源码实现:
/* 合并配置项操作 */ static char * ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module, ngx_uint_t ctx_index) { char *rv; ngx_uint_t s; ngx_http_conf_ctx_t *ctx, saved; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t **cscfp; cscfp = cmcf->servers.elts; ctx = (ngx_http_conf_ctx_t *) cf->ctx; saved = *ctx; rv = NGX_CONF_OK; /* 遍历每一个server{}块 */ for (s = 0; s < cmcf->servers.nelts; s++) { /* merge the server{}s' srv_conf's */ ctx->srv_conf = cscfp[s]->ctx->srv_conf; /* * 若定义了merge_srv_conf 方法; * 则进行http{}块下create_srv_conf 生成的结构体与遍历server{}块配置项生成的结构体进行merge_srv_conf操作; */ if (module->merge_srv_conf) { rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index], cscfp[s]->ctx->srv_conf[ctx_index]); if (rv != NGX_CONF_OK) { goto failed; } } /* * 若定义了merge_loc_conf 方法; * 则进行http{}块下create_loc_conf 生成的结构体与嵌套server{}块配置项生成的结构体进行merge_loc_conf操作; */ if (module->merge_loc_conf) { /* merge the server{}'s loc_conf */ ctx->loc_conf = cscfp[s]->ctx->loc_conf; rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index], cscfp[s]->ctx->loc_conf[ctx_index]); if (rv != NGX_CONF_OK) { goto failed; } /* merge the locations{}' loc_conf's */ /* * 若定义了merge_loc_conf 方法; * 则进行server{}块下create_loc_conf 生成的结构体与嵌套location{}块配置项生成的结构体进行merge_loc_conf操作; */ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; rv = ngx_http_merge_locations(cf, clcf->locations, cscfp[s]->ctx->loc_conf, module, ctx_index); if (rv != NGX_CONF_OK) { goto failed; } } } failed: *ctx = saved; return rv; } static char * ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index) { char *rv; ngx_queue_t *q; ngx_http_conf_ctx_t *ctx, saved; ngx_http_core_loc_conf_t *clcf; ngx_http_location_queue_t *lq; if (locations == NULL) { return NGX_CONF_OK; } ctx = (ngx_http_conf_ctx_t *) cf->ctx; saved = *ctx; /* * 若定义了merge_loc_conf 方法; * 则进行location{}块下create_loc_conf 生成的结构体与嵌套location{}块配置项生成的结构体进行merge_loc_conf操作; */ for (q = ngx_queue_head(locations); q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { lq = (ngx_http_location_queue_t *) q; clcf = lq->exact ? lq->exact : lq->inclusive; ctx->loc_conf = clcf->loc_conf; rv = module->merge_loc_conf(cf, loc_conf[ctx_index], clcf->loc_conf[ctx_index]); if (rv != NGX_CONF_OK) { return rv; } /* * 递归调用该函数; * 因为location{}继续内嵌location{} */ rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf, module, ctx_index); if (rv != NGX_CONF_OK) { return rv; } } *ctx = saved; return NGX_CONF_OK; }
处理自定义的配置
在文章中 《Nginx 模块开发》,我们给出了“Hello World” 的开发例子,在这个开发例子中,我们定义了自己的配置项,配置项名称的结构体定义如下:
typedef struct { ngx_str_t hello_string; ngx_int_t hello_counter; }ngx_http_hello_loc_conf_t;
为了处理我们定义的配置项结构,因此,我们把 ngx_command_t 结构体定义如下:
static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello_string"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1, ngx_http_hello_string, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_string), NULL }, { ngx_string("hello_counter"), NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_http_hello_counter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_counter), NULL }, ngx_null_command };
处理方法 ngx_http_hello_string 和ngx_http_hello_counter 定义如下:
static char * ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = ngx_conf_set_str_slot(cf, cmd, conf); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_string:%s", local_conf->hello_string.data); return rv; } static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = NULL; rv = ngx_conf_set_flag_slot(cf, cmd, conf); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_counter:%d", local_conf->hello_counter); return rv; }
参考资料:
《深入理解 Nginx 》
《nginx 启动阶段》
《Nginx高性能Web服务器详解》
以上就介绍了Nginx 配置解析,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。