1. 引言
Nginx 以其高性能、高并发和模块化的架构闻名。开发一个 Nginx 模块,核心任务之一就是定义模块可以识别的配置指令。ngx_command_t 结构体正是连接 Nginx 配置文件与模块内部逻辑的桥梁。本文将深入剖析 ngx_command_t 的各个字段、相关的宏定义,并通过一个完整的独立模块示例,重点讲解 NGX_HTTP_MAIN_CONF、NGX_HTTP_SRV_CONF、NGX_HTTP_LOC_CONF 等常见作用域,以及 NGX_CONF_FLAG、NGX_CONF_TAKE1 等类型配置参数的解析方法,并详细讲解内置的 ngx_conf_set_flag_slot、ngx_conf_set_str_slot 等 slot 函数。
2. ngx_command_t 结构体详解
ngx_command_t 定义在 src/core/ngx_conf_file.h 中,用于描述 Nginx 配置文件中的一个指令。
typedef struct ngx_command_s ngx_command_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; // 配置项在配置结构体中的偏移量(与 conf 配合使用)
void *post; // 后处理回调函数指针
};
2.1 字段说明
name: 指令名称,例如"my_proxy_pass"、"my_flag"。Nginx 在解析配置文件时,会匹配这个名称。type: 这是一个位掩码字段,用于组合多个属性。它决定了指令可以出现在哪些配置块(http、server、location)、可以接收多少个参数、参数的类型。将在下一节详细讲解。set: 这是一个函数指针,指向一个slot函数。当 Nginx 解析到该指令时,会调用这个函数来解析指令的参数,并将结果存储到对应的配置结构体中。Nginx 提供了许多内置的slot函数,如ngx_conf_set_flag_slot、ngx_conf_set_str_slot等,也可以自定义。conf: 用于指定当前指令属于哪个配置结构体(ngx_http_conf_ctx_t中的main_conf、srv_conf或loc_conf)。通常使用NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET宏。offset: 指定指令解析后的值存储在配置结构体中的具体偏移量。通常使用offsetof宏来计算。post: 一个void *指针,通常用于传递一个ngx_conf_post_t结构体,该结构体包含一个后处理回调函数指针,用于在参数解析完成后执行额外的校验或处理。
3. type 字段的宏定义与作用域
type 字段是 ngx_command_t 的核心,它通过位运算组合了多个宏。这些宏主要分为三类:作用域宏、参数个数宏 和 取值类型宏。
3.1 作用域宏
这些宏定义了指令可以出现在 Nginx 配置文件的哪些层级。
| 宏定义 | 说明 |
|---|---|
NGX_HTTP_MAIN_CONF | 指令可以出现在 http {} 块内 |
NGX_HTTP_SRV_CONF | 指令可以出现在 server {} 块内 |
NGX_HTTP_LOC_CONF | 指令可以出现在 location {} 块内 |
NGX_HTTP_UPS_CONF | 指令可以出现在 upstream {} 块内 |
NGX_ANY_CONF | 指令可以出现在任意配置层级 |
通常使用 | 组合多个作用域,例如 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF。
3.2 参数个数宏
这些宏定义了指令可以接收多少个参数(不包括指令名称本身)。
| 宏定义 | 说明 |
|---|---|
NGX_CONF_NOARGS | 指令不接收参数 |
NGX_CONF_TAKE1 | 指令接收 1 个参数 |
NGX_CONF_TAKE2 | 指令接收 2 个参数 |
NGX_CONF_TAKE3 | 指令接收 3 个参数 |
NGX_CONF_TAKE4 | 指令接收 4 个参数 |
NGX_CONF_TAKE5 | 指令接收 5 个参数 |
NGX_CONF_TAKE6 | 指令接收 6 个参数 |
NGX_CONF_TAKE7 | 指令接收 7 个参数 |
NGX_CONF_TAKE12 | 指令接收 1 或 2 个参数 |
NGX_CONF_TAKE13 | 指令接收 1 或 3 个参数 |
NGX_CONF_TAKE23 | 指令接收 2 或 3 个参数 |
NGX_CONF_TAKE123 | 指令接收 1、2 或 3 个参数 |
NGX_CONF_TAKE1234 | 指令接收 1、2、3 或 4 个参数 |
NGX_CONF_1MORE | 指令接收 1 个或更多参数 |
NGX_CONF_2MORE | 指令接收 2 个或更多参数 |
NGX_CONF_MULTI | 指令可以接收任意数量的参数(...) |
3.3 取值类型宏
这些宏定义了参数的类型,Nginx 会根据这些宏自动选择对应的内置 slot 函数进行解析。
| 宏定义 | 说明 |
|---|---|
NGX_CONF_FLAG | 参数为 on 或 off,解析为 ngx_flag_t(即 0 或 1) |
NGX_CONF_ANY | 参数可以是任意字符串 |
NGX_CONF_BLOCK | 指令是一个块指令,后面跟着 {} |
NGX_CONF_TAKE1 等 | 也可以作为取值类型,表示参数个数 |
3.4 常见 Nginx 指令的 type 字段组合示例
下表展示了几个常见 Nginx 指令的 type 字段是如何由作用域宏、参数个数宏和取值类型宏组合而成的,帮助读者将理论知识与实际配置指令对应起来。
| 指令 | 作用域宏 | 参数个数宏 | 取值类型宏 | 完整 type 组合 | 对应内置 slot 函数 |
|---|---|---|---|---|---|
proxy_pass | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | — | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | ngx_conf_set_str_slot |
proxy_set_header | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2 | — | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2 | ngx_conf_set_keyval_slot |
client_max_body_size | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | — | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | ngx_conf_set_size_slot |
proxy_redirect | NGX_HTTP_HTTP_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | NGX_CONF_FLAG | NGX_HTTP_HTTP_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | NGX_CONF_FLAG | ngx_conf_set_flag_slot |
proxy_connect_timeout | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | — | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | ngx_conf_set_msec_slot |
worker_processes | NGX_MAIN_CONF | NGX_CONF_TAKE1 | — | NGX_MAIN_CONF | NGX_CONF_TAKE1 | ngx_conf_set_num_slot |
说明:
proxy_pass只允许在location块内出现,接收 1 个 URL 字符串参数,因此使用NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1。proxy_set_header可在http、server、location三个层级出现,接收 2 个参数(键和值),因此组合了三个作用域宏和NGX_CONF_TAKE2。client_max_body_size同样支持三个层级,接收 1 个大小参数,使用ngx_conf_set_size_slot解析。proxy_redirect比较特殊,它既可以接收一个字符串参数(如off),也可以接收default或自定义替换规则,因此type中同时包含了NGX_CONF_TAKE1和NGX_CONF_FLAG,使得ngx_conf_set_flag_slot能同时处理on/off和普通字符串。worker_processes是全局指令(NGX_MAIN_CONF),不属于 HTTP 模块,因此作用域宏为NGX_MAIN_CONF。
4. 内置 Slot 函数详解
slot 函数是 ngx_command_t 中 set 字段指向的函数。Nginx 提供了丰富的内置 slot 函数,用于处理常见的配置解析场景。
4.1 ngx_conf_set_flag_slot
- 用途: 解析
on/off类型的配置项。 - 对应 type:
NGX_CONF_FLAG。 - 存储类型:
ngx_flag_t(本质是ngx_int_t)。 - 示例:
proxy_pass http://backend;中的proxy_pass不是 flag,但proxy_set_header X-Real-IP $remote_addr;中的proxy_set_header也不是。一个典型的 flag 指令如proxy_redirect off;。
4.2 ngx_conf_set_str_slot
- 用途: 解析一个字符串参数。
- 对应 type:
NGX_CONF_TAKE1。 - 存储类型:
ngx_str_t。 - 示例:
proxy_pass http://backend;中的 URL 部分。
4.3 ngx_conf_set_num_slot
- 用途: 解析一个数字参数。
- 对应 type:
NGX_CONF_TAKE1。 - 存储类型:
ngx_int_t。 - 示例:
worker_processes 4;中的4。
4.4 ngx_conf_set_size_slot
- 用途: 解析一个大小参数(如
10m、1k)。 - 对应 type:
NGX_CONF_TAKE1。 - 存储类型:
size_t。 - 示例:
client_max_body_size 10m;中的10m。
4.5 ngx_conf_set_msec_slot
- 用途: 解析一个毫秒级时间参数(如
10s、100ms)。 - 对应 type:
NGX_CONF_TAKE1。 - 存储类型:
ngx_msec_t(本质是ngx_uint_t)。 - 示例:
proxy_connect_timeout 10s;中的10s。
4.6 ngx_conf_set_sec_slot
- 用途: 解析一个秒级时间参数。
- 对应 type:
NGX_CONF_TAKE1。 - 存储类型:
time_t。
4.7 ngx_conf_set_keyval_slot
- 用途: 解析键值对参数列表,如
proxy_set_header X-Real-IP $remote_addr;。 - 对应 type:
NGX_CONF_TAKE2。 - 存储类型:
ngx_array_t(元素为ngx_keyval_t)。
5. 实战:构建一个完整的 Nginx 模块
创建一个名为 ngx_http_my_module 的模块,它包含一个 location 级别的配置,用于演示 flag、str、num 等类型的配置解析。
5.1 项目结构
my_module/
├── config
└── ngx_http_my_module.c
5.2 config 文件
config 文件用于 Nginx 的 --add-module 编译系统。
# my_module/config
ngx_addon_name=ngx_http_my_module
HTTP_MODULES="$HTTP_MODULES ngx_http_my_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_my_module.c"
5.3 模块源代码
// my_module/ngx_http_my_module.c
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
// 1. 定义 location 级别的配置结构体
typedef struct {
ngx_flag_t enable; // 对应 my_flag on|off;
ngx_str_t target; // 对应 my_target /path;
ngx_int_t count; // 对应 my_count 10;
} ngx_http_my_loc_conf_t;
// 2. 创建 location 配置结构体的函数
static void *ngx_http_my_create_loc_conf(ngx_conf_t *cf) {
ngx_http_my_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_my_loc_conf_t));
if (conf == NULL) {
return NULL;
}
// 设置默认值
conf->enable = NGX_CONF_UNSET; // 未设置
conf->count = NGX_CONF_UNSET; // 未设置
return conf;
}
// 3. 合并 location 配置的函数(用于继承父级配置)
static char *ngx_http_my_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) {
ngx_http_my_loc_conf_t *prev = parent;
ngx_http_my_loc_conf_t *conf = child;
ngx_conf_merge_value(conf->enable, prev->enable, 0); // 默认关闭
ngx_conf_merge_str_value(conf->target, prev->target, "/default");
ngx_conf_merge_value(conf->count, prev->count, 5);
return NGX_CONF_OK;
}
// 4. 定义指令数组
static ngx_command_t ngx_http_my_commands[] = {
{ ngx_string("my_flag"),
NGX_HTTP_LOC_CONF | NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_my_loc_conf_t, enable),
NULL },
{ ngx_string("my_target"),
NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_my_loc_conf_t, target),
NULL },
{ ngx_string("my_count"),
NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_my_loc_conf_t, count),
NULL },
ngx_null_command // 指令数组结束标志
};
// 5. 定义 HTTP 模块上下文
static ngx_http_module_t ngx_http_my_module_ctx = {
NULL, // preconfiguration
NULL, // postconfiguration
NULL, // create main configuration
NULL, // init main configuration
NULL, // create server configuration
NULL, // merge server configuration
ngx_http_my_create_loc_conf, // create location configuration
ngx_http_my_merge_loc_conf // merge location configuration
};
// 6. 定义模块
ngx_module_t ngx_http_my_module = {
NGX_MODULE_V1,
&ngx_http_my_module_ctx, // module context
ngx_http_my_commands, // module directives
NGX_HTTP_MODULE, // module type
NULL, // init master
NULL, // init module
NULL, // init process
NULL, // init thread
NULL, // exit thread
NULL, // exit process
NULL, // exit master
NGX_MODULE_V1_PADDING
};
5.4 编译与配置
-
编译 Nginx:
./configure --add-module=/path/to/my_module make sudo make install -
配置 nginx.conf:
http { server { listen 80; server_name example.com; location /test { my_flag on; my_target /api; my_count 10; # ... 其他配置 } } } -
在模块中使用配置:
在ngx_http_my_handler等处理函数中,可以通过ngx_http_get_module_loc_conf(r, ngx_http_my_module)获取配置结构体,然后读取conf->enable、conf->target、conf->count等字段。
5.5 模块处理函数示例
为了让模块真正处理 HTTP 请求,我们需要注册一个 handler 函数。下面在 ngx_http_my_module.c 中补充 ngx_http_my_handler,演示如何获取配置项并返回响应。
// 在 ngx_http_my_module.c 中补充以下内容
// 7. 注册 handler 的 postconfiguration 函数
static ngx_int_t ngx_http_my_postconfiguration(ngx_conf_t *cf) {
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
// 在 HTTP 内容阶段注册 handler(优先级 NGX_HTTP_CONTENT_PHASE)
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_my_handler;
return NGX_OK;
}
// 8. 请求处理函数
static ngx_int_t ngx_http_my_handler(ngx_http_request_t *r) {
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
ngx_http_my_loc_conf_t *mlcf;
// 仅处理 GET 和 HEAD 请求
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
// 丢弃请求体
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
// 获取 location 级别的配置
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_my_module);
// 构造响应内容
// 使用 ngx_pnalloc 分配缓冲区
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
// 根据配置项生成响应字符串
u_char *response;
size_t response_len;
if (mlcf->enable) {
response_len = ngx_snprintf(NULL, 0,
"my_flag is ON\nmy_target: %V\nmy_count: %i\n",
&mlcf->target, mlcf->count);
response = ngx_pnalloc(r->pool, response_len);
if (response == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_snprintf(response, response_len,
"my_flag is ON\nmy_target: %V\nmy_count: %i\n",
&mlcf->target, mlcf->count);
} else {
response = (u_char *) "my_flag is OFF\n";
response_len = ngx_strlen(response);
}
b->pos = response;
b->last = response + response_len;
b->memory = 1; // 内容在内存中
b->last_buf = 1; // 最后一个缓冲区
out.buf = b;
out.next = NULL;
// 设置响应头
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = response_len;
ngx_str_set(&r->headers_out.content_type, "text/plain");
// 发送响应头
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
// 发送响应体
return ngx_http_output_filter(r, &out);
}
代码说明:
ngx_http_my_postconfiguration:在 Nginx 启动阶段将ngx_http_my_handler注册到 HTTP 内容阶段(NGX_HTTP_CONTENT_PHASE),这样当请求匹配到location时,Nginx 会自动调用该函数。ngx_http_my_handler:核心处理函数。首先过滤非 GET/HEAD 请求,然后通过ngx_http_get_module_loc_conf获取之前定义的配置结构体,根据mlcf->enable的值决定响应内容,最后构造缓冲区并发送响应头和响应体。
更新模块上下文:别忘了在 ngx_http_my_module_ctx 中将 postconfiguration 字段指向 ngx_http_my_postconfiguration:
static ngx_http_module_t ngx_http_my_module_ctx = {
NULL, // preconfiguration
ngx_http_my_postconfiguration, // postconfiguration ← 修改此处
NULL, // create main configuration
NULL, // init main configuration
NULL, // create server configuration
NULL, // merge server configuration
ngx_http_my_create_loc_conf, // create location configuration
ngx_http_my_merge_loc_conf // merge location configuration
};
完成上述修改后,重新编译 Nginx 并启动。访问 http://example.com/test,如果配置了 my_flag on;,将返回:
my_flag is ON
my_target: /api
my_count: 10
如果 my_flag off; 或未配置,则返回:
my_flag is OFF
6. 总结
ngx_command_t 是 Nginx 模块开发的基石。理解其 name、type、set、conf、offset 和 post 字段,以及 type 字段中作用域、参数个数和取值类型的宏定义,是编写高质量 Nginx 模块的关键。通过合理利用 Nginx 提供的内置 slot 函数(如 ngx_conf_set_flag_slot、ngx_conf_set_str_slot 等),我们可以高效地解析常见的配置模式,从而专注于模块的核心逻辑。希望本文的讲解和示例能帮助你快速上手 Nginx 模块开发。
7. 附录:name 在不同作用域下重复的完整示例
Nginx 允许在不同配置层级(http、server、location)定义同名指令,每个层级的指令解析后存储到各自对应的配置结构体中。下面提供一个可直接复制编译运行的完整模块,演示 my_timeout 指令在 http、server、location 三个作用域下重复定义,并展示各层级配置值的继承与覆盖关系。
7.1 项目结构
my_scope_module/
├── config
└── ngx_http_my_scope_module.c
7.2 config 文件
# my_scope_module/config
ngx_addon_name=ngx_http_my_scope_module
HTTP_MODULES="$HTTP_MODULES ngx_http_my_scope_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_my_scope_module.c"
7.3 完整源代码(可直接复制编译)
// my_scope_module/ngx_http_my_scope_module.c
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
// ========== 1. 三个层级的配置结构体 ==========
// main 级别(http 块)
typedef struct {
ngx_msec_t timeout; // 单位毫秒
} ngx_http_my_scope_main_conf_t;
// server 级别
typedef struct {
ngx_msec_t timeout;
} ngx_http_my_scope_srv_conf_t;
// location 级别
typedef struct {
ngx_msec_t timeout;
} ngx_http_my_scope_loc_conf_t;
// ========== 2. 创建配置结构体 ==========
static void *ngx_http_my_scope_create_main_conf(ngx_conf_t *cf) {
ngx_http_my_scope_main_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_my_scope_main_conf_t));
if (conf == NULL) {
return NULL;
}
conf->timeout = NGX_CONF_UNSET_MSEC;
return conf;
}
static void *ngx_http_my_scope_create_srv_conf(ngx_conf_t *cf) {
ngx_http_my_scope_srv_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_my_scope_srv_conf_t));
if (conf == NULL) {
return NULL;
}
conf->timeout = NGX_CONF_UNSET_MSEC;
return conf;
}
static void *ngx_http_my_scope_create_loc_conf(ngx_conf_t *cf) {
ngx_http_my_scope_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_my_scope_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->timeout = NGX_CONF_UNSET_MSEC;
return conf;
}
// ========== 3. 合并配置(继承父级值) ==========
static char *ngx_http_my_scope_merge_main_conf(ngx_conf_t *cf,
void *parent, void *child) {
ngx_http_my_scope_main_conf_t *prev = parent;
ngx_http_my_scope_main_conf_t *conf = child;
ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 5000); // 默认 5s
return NGX_CONF_OK;
}
static char *ngx_http_my_scope_merge_srv_conf(ngx_conf_t *cf,
void *parent, void *child) {
ngx_http_my_scope_srv_conf_t *prev = parent;
ngx_http_my_scope_srv_conf_t *conf = child;
ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 3000); // 默认 3s
return NGX_CONF_OK;
}
static char *ngx_http_my_scope_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child) {
ngx_http_my_scope_loc_conf_t *prev = parent;
ngx_http_my_scope_loc_conf_t *conf = child;
ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 1000); // 默认 1s
return NGX_CONF_OK;
}
// ========== 4. 定义指令数组(同名指令 my_timeout 出现在三个作用域) ==========
static ngx_command_t ngx_http_my_scope_commands[] = {
// http 块级别:my_timeout 10s;
{ ngx_string("my_timeout"),
NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_my_scope_main_conf_t, timeout),
NULL },
// server 块级别:my_timeout 5s;
{ ngx_string("my_timeout"),
NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_my_scope_srv_conf_t, timeout),
NULL },
// location 块级别:my_timeout 1s;
{ ngx_string("my_timeout"),
NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_my_scope_loc_conf_t, timeout),
NULL },
ngx_null_command
};
// ========== 5. 注册 handler 并输出各层级配置值 ==========
static ngx_int_t ngx_http_my_scope_handler(ngx_http_request_t *r) {
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
ngx_http_my_scope_main_conf_t *mmcf;
ngx_http_my_scope_srv_conf_t *mscf;
ngx_http_my_scope_loc_conf_t *mlcf;
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
// 获取三个层级的配置
mmcf = ngx_http_get_module_main_conf(r, ngx_http_my_scope_module);
mscf = ngx_http_get_module_srv_conf(r, ngx_http_my_scope_module);
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_my_scope_module);
// 构造响应:展示各层级 my_timeout 的值
u_char response[256];
size_t response_len;
response_len = ngx_snprintf(response, sizeof(response),
"my_timeout values by scope:\n"
" http level: %l ms\n"
" server level: %l ms\n"
" location level: %l ms\n",
mmcf->timeout, mscf->timeout, mlcf->timeout);
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->pos = response;
b->last = response + response_len;
b->memory = 1;
b->last_buf = 1;
out.buf = b;
out.next = NULL;
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = response_len;
ngx_str_set(&r->headers_out.content_type, "text/plain");
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK) {
return rc;
}
return ngx_http_output_filter(r, &out);
}
// ========== 6. postconfiguration 注册 handler ==========
static ngx_int_t ngx_http_my_scope_postconfiguration(ngx_conf_t *cf) {
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_my_scope_handler;
return NGX_OK;
}
// ========== 7. 模块上下文 ==========
static ngx_http_module_t ngx_http_my_scope_module_ctx = {
NULL, // preconfiguration
ngx_http_my_scope_postconfiguration, // postconfiguration
ngx_http_my_scope_create_main_conf, // create main configuration
ngx_http_my_scope_merge_main_conf, // merge main configuration
ngx_http_my_scope_create_srv_conf, // create server configuration
ngx_http_my_scope_merge_srv_conf, // merge server configuration
ngx_http_my_scope_create_loc_conf, // create location configuration
ngx_http_my_scope_merge_loc_conf // merge location configuration
};
// ========== 8. 模块定义 ==========
ngx_module_t ngx_http_my_scope_module = {
NGX_MODULE_V1,
&ngx_http_my_scope_module_ctx,
ngx_http_my_scope_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
7.4 编译与测试
# 编译
./configure --add-module=/path/to/my_scope_module
make
sudo make install
在 nginx.conf 中配置:
http {
my_timeout 10s; # http 级别:10 秒
server {
listen 80;
server_name example.com;
my_timeout 5s; # server 级别:5 秒(覆盖 http 的 10s)
location /test {
my_timeout 1s; # location 级别:1 秒(覆盖 server 的 5s)
}
location /inherit {
# 未设置 my_timeout,继承 server 级别的 5s
}
}
}
访问测试:
# 访问 /test,输出:
# http level: 10000 ms
# server level: 5000 ms
# location level: 1000 ms
curl http://example.com/test
# 访问 /inherit,输出:
# http level: 10000 ms
# server level: 5000 ms
# location level: 5000 ms ← 继承自 server
curl http://example.com/inherit
7.5 关键要点
- 同名指令:
my_timeout在ngx_http_my_scope_commands[]中出现了三次,分别对应NGX_HTTP_MAIN_CONF、NGX_HTTP_SRV_CONF、NGX_HTTP_LOC_CONF作用域。 - 独立存储:每个作用域有独立的配置结构体(
main_conf、srv_conf、loc_conf),通过conf字段(NGX_HTTP_MAIN_CONF_OFFSET等)区分。 - 继承机制:通过
ngx_conf_merge_msec_value实现层级继承——子层级未设置时自动使用父层级的值。 ngx_http_get_module_*_conf:三个不同的获取函数分别获取对应层级的配置值,互不干扰。

342

被折叠的 条评论
为什么被折叠?



