4. Nginx 模块开发:深入理解 ngx_command_t 结构体与配置指令解析

1. 引言

Nginx 以其高性能、高并发和模块化的架构闻名。开发一个 Nginx 模块,核心任务之一就是定义模块可以识别的配置指令。ngx_command_t 结构体正是连接 Nginx 配置文件与模块内部逻辑的桥梁。本文将深入剖析 ngx_command_t 的各个字段、相关的宏定义,并通过一个完整的独立模块示例,重点讲解 NGX_HTTP_MAIN_CONFNGX_HTTP_SRV_CONFNGX_HTTP_LOC_CONF 等常见作用域,以及 NGX_CONF_FLAGNGX_CONF_TAKE1 等类型配置参数的解析方法,并详细讲解内置的 ngx_conf_set_flag_slotngx_conf_set_str_slotslot 函数。

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: 这是一个位掩码字段,用于组合多个属性。它决定了指令可以出现在哪些配置块(httpserverlocation)、可以接收多少个参数、参数的类型。将在下一节详细讲解。
  • set: 这是一个函数指针,指向一个 slot 函数。当 Nginx 解析到该指令时,会调用这个函数来解析指令的参数,并将结果存储到对应的配置结构体中。Nginx 提供了许多内置的 slot 函数,如 ngx_conf_set_flag_slotngx_conf_set_str_slot 等,也可以自定义。
  • conf: 用于指定当前指令属于哪个配置结构体(ngx_http_conf_ctx_t 中的 main_confsrv_confloc_conf)。通常使用 NGX_HTTP_MAIN_CONF_OFFSETNGX_HTTP_SRV_CONF_OFFSETNGX_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参数为 onoff,解析为 ngx_flag_t(即 01
NGX_CONF_ANY参数可以是任意字符串
NGX_CONF_BLOCK指令是一个块指令,后面跟着 {}
NGX_CONF_TAKE1也可以作为取值类型,表示参数个数

3.4 常见 Nginx 指令的 type 字段组合示例

下表展示了几个常见 Nginx 指令的 type 字段是如何由作用域宏、参数个数宏和取值类型宏组合而成的,帮助读者将理论知识与实际配置指令对应起来。

指令作用域宏参数个数宏取值类型宏完整 type 组合对应内置 slot 函数
proxy_passNGX_HTTP_LOC_CONFNGX_CONF_TAKE1NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1ngx_conf_set_str_slot
proxy_set_headerNGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONFNGX_CONF_TAKE2NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2ngx_conf_set_keyval_slot
client_max_body_sizeNGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONFNGX_CONF_TAKE1NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1ngx_conf_set_size_slot
proxy_redirectNGX_HTTP_HTTP_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONFNGX_CONF_TAKE1NGX_CONF_FLAGNGX_HTTP_HTTP_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | NGX_CONF_FLAGngx_conf_set_flag_slot
proxy_connect_timeoutNGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONFNGX_CONF_TAKE1NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1ngx_conf_set_msec_slot
worker_processesNGX_MAIN_CONFNGX_CONF_TAKE1NGX_MAIN_CONF | NGX_CONF_TAKE1ngx_conf_set_num_slot

说明

  • proxy_pass 只允许在 location 块内出现,接收 1 个 URL 字符串参数,因此使用 NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1
  • proxy_set_header 可在 httpserverlocation 三个层级出现,接收 2 个参数(键和值),因此组合了三个作用域宏和 NGX_CONF_TAKE2
  • client_max_body_size 同样支持三个层级,接收 1 个大小参数,使用 ngx_conf_set_size_slot 解析。
  • proxy_redirect 比较特殊,它既可以接收一个字符串参数(如 off),也可以接收 default 或自定义替换规则,因此 type 中同时包含了 NGX_CONF_TAKE1NGX_CONF_FLAG,使得 ngx_conf_set_flag_slot 能同时处理 on/off 和普通字符串。
  • worker_processes 是全局指令(NGX_MAIN_CONF),不属于 HTTP 模块,因此作用域宏为 NGX_MAIN_CONF

4. 内置 Slot 函数详解

slot 函数是 ngx_command_tset 字段指向的函数。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

  • 用途: 解析一个大小参数(如 10m1k)。
  • 对应 type: NGX_CONF_TAKE1
  • 存储类型: size_t
  • 示例: client_max_body_size 10m; 中的 10m

4.5 ngx_conf_set_msec_slot

  • 用途: 解析一个毫秒级时间参数(如 10s100ms)。
  • 对应 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 级别的配置,用于演示 flagstrnum 等类型的配置解析。

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 编译与配置

  1. 编译 Nginx:

    ./configure --add-module=/path/to/my_module
    make
    sudo make install
    
  2. 配置 nginx.conf:

    http {
        server {
            listen 80;
            server_name example.com;
    
            location /test {
                my_flag on;
                my_target /api;
                my_count 10;
                # ... 其他配置
            }
        }
    }
    
  3. 在模块中使用配置:
    ngx_http_my_handler 等处理函数中,可以通过 ngx_http_get_module_loc_conf(r, ngx_http_my_module) 获取配置结构体,然后读取 conf->enableconf->targetconf->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 模块开发的基石。理解其 nametypesetconfoffsetpost 字段,以及 type 字段中作用域、参数个数和取值类型的宏定义,是编写高质量 Nginx 模块的关键。通过合理利用 Nginx 提供的内置 slot 函数(如 ngx_conf_set_flag_slotngx_conf_set_str_slot 等),我们可以高效地解析常见的配置模式,从而专注于模块的核心逻辑。希望本文的讲解和示例能帮助你快速上手 Nginx 模块开发。

7. 附录:name 在不同作用域下重复的完整示例

Nginx 允许在不同配置层级(httpserverlocation)定义同名指令,每个层级的指令解析后存储到各自对应的配置结构体中。下面提供一个可直接复制编译运行的完整模块,演示 my_timeout 指令在 httpserverlocation 三个作用域下重复定义,并展示各层级配置值的继承与覆盖关系。

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_timeoutngx_http_my_scope_commands[] 中出现了三次,分别对应 NGX_HTTP_MAIN_CONFNGX_HTTP_SRV_CONFNGX_HTTP_LOC_CONF 作用域。
  • 独立存储:每个作用域有独立的配置结构体(main_confsrv_confloc_conf),通过 conf 字段(NGX_HTTP_MAIN_CONF_OFFSET 等)区分。
  • 继承机制:通过 ngx_conf_merge_msec_value 实现层级继承——子层级未设置时自动使用父层级的值。
  • ngx_http_get_module_*_conf:三个不同的获取函数分别获取对应层级的配置值,互不干扰。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chexus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值