1. Nginx 模块化开发概述

Nginx 的高性能与灵活扩展性源于其高度模块化的内部架构。从开发者的视角看,每一个功能都由一个独立的模块实现,模块在编译时被链接进 Nginx 核心,或者通过 --add-module 方式在编译时动态加入。理解不同模块类型的职责、数据结构与钩子挂载方式,是掌握 Nginx 自定义开发的基础。

Nginx 模块在代码层面分为以下几大类:

  • 核心模块:提供全局配置与框架支撑,如 ngx_core_module
  • 事件模块:封装底层网络 I/O 模型,如 ngx_epoll_module
  • HTTP 模块:按处理阶段进一步细分为 Handler 模块Filter 模块Upstream 模块
  • Mail 模块:处理邮件代理协议。
  • Stream 模块:负责四层 TCP/UDP 代理。

各类模块在源码中的结构体定义、上下文注册方式、指令挂载位置均有差异。本文从实例出发,逐一剖析每种模块的开发范式。

2. 核心模块开发范式

核心模块定义了 Nginx 的顶层配置结构,并负责解析全局指令(如 worker_processeserror_log)。开发核心模块时,需要实现 ngx_core_module_t 类型的结构体,并挂载 create_conf 等回调。

一个最简单的核心模块示例:

static ngx_core_module_t ngx_example_core_module_ctx = {
    ngx_string("example"),
    NULL,                  /* create_conf */
    NULL                   /* init_conf */
};

ngx_module_t ngx_example_core_module = {
    NGX_MODULE_V1,
    &ngx_example_core_module_ctx,   /* module context */
    NULL,                           /* module directives (ngx_command_t array) */
    NGX_CORE_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
};

核心模块的 module type 填写 NGX_CORE_MODULE,其上下文指针必须指向 ngx_core_module_t。这类模块通常较底层,开发自定义核心模块的场景较少,但它是理解 Nginx 模块注册机制的起点。

编译配置

核心模块负责全局配置,编译时添加到 CORE_MODULES

ngx_addon_name=ngx_example_core_module
CORE_MODULES="$CORE_MODULES ngx_example_core_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_example_core_module.c"

3. 事件模块开发范式

事件模块负责封装 epollkqueue 等系统调用,向上层提供统一的异步事件接口。开发事件模块时,需要实现 ngx_event_module_t 上下文,并提供 ngx_event_actions_t 结构体中的回调函数(如 adddelprocess_events)。

代码骨架如下:

static ngx_int_t ngx_example_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) {
    /* 将事件注册到底层 I/O 复用模型 */
    return NGX_OK;
}

static ngx_int_t ngx_example_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) {
    /* 等待并处理活跃事件 */
    return NGX_OK;
}

static ngx_event_module_t ngx_example_event_module_ctx = {
    &example_event_name,
    NULL,                       /* create configuration */
    NULL,                       /* init configuration */

    {
        ngx_example_add_event,
        NULL,                   /* del_connection */
        NULL,                   /* del_event */
        NULL,                   /* enable accept */
        NULL,                   /* disable accept */
        ngx_example_process_events,
        NULL,                   /* init */
        NULL                    /* done */
    }
};

ngx_module_t ngx_example_event_module = {
    NGX_MODULE_V1,
    &ngx_example_event_module_ctx,
    NULL,                       /* 指令表 */
    NGX_EVENT_MODULE,           /* module type */
    NULL, NULL, NULL, NULL, NULL, NULL,
    NGX_MODULE_V1_PADDING
};

事件模块的 type 成员为 NGX_EVENT_MODULE,上下文字段对应 ngx_event_module_t。如果你希望 Nginx 支持新的 I/O 模型,就需要实现一个事件模块,通常与平台相关。

编译配置

事件模块封装 I/O 复用,挂到 EVENT_MODULES

ngx_addon_name=ngx_example_event_module
EVENT_MODULES="$EVENT_MODULES ngx_example_event_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_example_event_module.c"

4. HTTP 模块开发范式

HTTP 模块是开发中最常接触的类型,所有与 HTTP 请求处理相关的模块都属于这个范畴。在源码层面,HTTP 模块分为三种角色:Handler、Filter、Upstream,它们在同一个 NGX_HTTP_MODULE 类型下通过不同的钩子挂载点和处理函数来区分。

4.1 HTTP Handler 模块

Handler 模块直接生成响应或转发请求,是请求的最终“内容生产者”。开发一个简单的 handler 模块需要完成以下步骤:

  1. 定义模块指令表(ngx_command_t),可在配置中设置参数。
  2. 实现 Handler 函数,负责填充 ngx_http_request_t 并返回响应。
  3. ngx_http_module_t 上下文的 postconfigurationpreconfiguration 中,将 Handler 挂载到对应 location 的阶段。

一个完整的 handler 示例——输出“Hello, Nginx!”:

static ngx_int_t
ngx_example_handler(ngx_http_request_t *r) {
    ngx_buf_t    *b;
    ngx_chain_t   out;

    if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
        return NGX_HTTP_NOT_ALLOWED;
    }

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = 13;
    r->headers_out.content_type = ngx_palloc(r->pool, sizeof(ngx_str_t));
    if (r->headers_out.content_type == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    r->headers_out.content_type->len = sizeof("text/plain") - 1;
    r->headers_out.content_type->data = (u_char *) "text/plain";

    b = ngx_create_temp_buf(r->pool, 13);
    if (b == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    ngx_memcpy(b->pos, "Hello, Nginx!", 13);
    b->last = b->pos + 13;
    b->last_buf = 1;

    out.buf = b;
    out.next = NULL;

    ngx_http_send_header(r);
    return ngx_http_output_filter(r, &out);
}

static ngx_command_t ngx_example_commands[] = {
    { ngx_string("example"),
      NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
      ngx_http_set_slot,
      0,
      0,
      NULL },
    ngx_null_command
};

static ngx_http_module_t ngx_example_module_ctx = {
    NULL,                                /* preconfiguration */
    NULL,                                /* postconfiguration */
    NULL,                                /* create main configuration */
    NULL,                                /* init main configuration */
    NULL,                                /* create server configuration */
    NULL,                                /* merge server configuration */
    NULL,                                /* create location configuration */
    NULL                                 /* merge location configuration */
};

ngx_module_t ngx_example_module = {
    NGX_MODULE_V1,
    &ngx_example_module_ctx,
    ngx_example_commands,
    NGX_HTTP_MODULE,
    NULL, NULL, NULL, NULL, NULL, NULL,
    NGX_MODULE_V1_PADDING
};

Handler 必须返回具体的 HTTP 状态码,并通过 ngx_http_output_filter 将响应链发送出去。同一个 location 只能有一个 Handler 最终处理请求(即 content handler),这是 Handler 模块与 Filter 模块最核心的区别。

4.2 HTTP Filter 模块

Filter 模块在 Handler 生成响应后对输出进行二次加工,例如 gzip 压缩、添加响应头、响应体替换等。Filter 模块通过插入到全局的 ngx_http_output_filter_t 过滤器链中工作,多个 filter 可以叠加,数据依次流经各 filter。

开发一个简单的 filter 模块——在所有响应末尾追加一行注释:

static ngx_int_t
ngx_example_filter_body(ngx_http_request_t *r, ngx_chain_t *in) {
    ngx_buf_t    *b;
    ngx_chain_t  *cl, *ln;
    const char    footer[] = "\n<!-- generated by example filter -->";

    /* 将原始链暂存,然后追加自定义内容 */
    b = ngx_create_temp_buf(r->pool, sizeof(footer) - 1);
    if (b == NULL) {
        return NGX_ERROR;
    }
    ngx_memcpy(b->pos, footer, sizeof(footer) - 1);
    b->last = b->pos + sizeof(footer) - 1;
    b->last_buf = 1;

    cl = ngx_alloc_chain_link(r->pool);
    if (cl == NULL) {
        return NGX_ERROR;
    }
    cl->buf = b;
    cl->next = NULL;

    /* 将追加链挂到原始链末尾 */
    for (ln = in; ln->next; ln = ln->next) { /* void */ }
    ln->next = cl;

    return ngx_http_next_body_filter(r, in);
}

static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t
ngx_example_filter_init(ngx_conf_t *cf) {
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_header_filter  = ngx_example_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;
    ngx_http_top_body_filter   = ngx_example_filter_body;

    return NGX_OK;
}

Filter 模块的关键在于通过全局变量(如 ngx_http_top_body_filter)将自身处理函数插入过滤器链顶端,并在函数末尾调用 ngx_http_next_body_filter 将数据传递给下一个 filter。Filter 模块没有独占 location 的特性,它们可以作用于所有经过的响应,这是与 Handler 的本质区别。

4.3 HTTP Upstream 模块

Upstream 模块定义后端服务器组,并提供负载均衡算法。严格来说,普通的 Upstream 使用并不需要编写新的模块类型,Nginx 内置的 ngx_http_upstream_module 已经提供了轮询、ip_hash 等算法。但如果需要实现自定义的复杂负载均衡逻辑(如加权一致性哈希、动态负载等),就需要开发一个 Upstream 模块。

开发 Upstream 模块的要点是实现 ngx_http_upstream_peer_t 接口中的 getfreenotify 等函数,并在配置解析时将自定义 peer 结构挂载到 upstream 的 peer.init 回调中。以下示意自定义 peer 的骨架:

typedef struct {
    ngx_http_upstream_peer_t   peer;
    ngx_array_t                servers;  /* 自定义服务器列表 */
    /* 其他自定义加权、哈希数据 */
} ngx_example_upstream_peer_t;

static ngx_int_t
ngx_example_upstream_get_peer(ngx_peer_connection_t *pc, void *data) {
    ngx_example_upstream_peer_t *ep = data;

    /* 根据自定义算法选择后端并填充 pc->sockaddr */
    ...

    return NGX_OK;
}

完整的 Upstream 模块需要注册为 NGX_HTTP_MODULE 类型,并在 postconfiguration 中调用 ngx_http_upstream_init_round_robin 等初始化函数,最后替换 peer.init。此部分较复杂,但思路仍是围绕 ngx_http_upstream_peer_t 实现算法。

6. Stream 模块开发

Stream 模块处理 TCP/UDP 四层代理,自 1.9.0 起成为正式功能。从开发视角,Stream 模块的类型为 NGX_STREAM_MODULE,上下文结构体为 ngx_stream_module_t。与 HTTP 模块类似,Stream 模块内部也可以区分 Handler(如 proxy_pass)、Filter(如 stream_ssl_module)和 Upstream。

一个简单的 Stream handler 模块示例——实现 TCP echo:

static ngx_int_t
ngx_example_stream_handler(ngx_stream_session_t *s) {
    ngx_connection_t  *c = s->connection;
    ngx_buf_t         *b;
    ngx_chain_t       *cl;

    /* 分配缓冲区 */
    b = ngx_create_temp_buf(c->pool, s->buffer->last - s->buffer->pos);
    if (b == NULL) {
        return NGX_ERROR;
    }
    ngx_memcpy(b->pos, s->buffer->pos, s->buffer->last - s->buffer->pos);
    b->last = b->pos + (s->buffer->last - s->buffer->pos);
    b->last_buf = 1;

    cl = ngx_alloc_chain_link(c->pool);
    if (cl == NULL) {
        return NGX_ERROR;
    }
    cl->buf = b;
    cl->next = NULL;

    /* 直接回写接收到的数据 */
    c->write->handler = ngx_stream_write_filter;
    ngx_stream_top_filter(c->write, cl);
    return NGX_OK;
}

static ngx_command_t ngx_example_stream_commands[] = {
    { ngx_string("stream_example"),
      NGX_STREAM_SRV_CONF | NGX_CONF_NOARGS,
      ngx_stream_set_var,    /* 简化示意 */
      0,
      0,
      NULL },
    ngx_null_command
};

static ngx_stream_module_t ngx_example_stream_ctx = {
    NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

ngx_module_t ngx_example_stream_module = {
    NGX_MODULE_V1,
    &ngx_example_stream_ctx,
    ngx_example_stream_commands,
    NGX_STREAM_MODULE,
    NULL, NULL, NULL, NULL, NULL, NULL,
    NGX_MODULE_V1_PADDING
};

Stream handler 与 HTTP handler 的流程高度相似,只是操作的对象从 ngx_http_request_t 变成了 ngx_stream_session_t,模块类型也从 NGX_HTTP_MODULE 变为 NGX_STREAM_MODULE

7. 模块类型区别总结

模块类型 模块类型常量 上下文结构体 主要任务 开发重点
核心模块 NGX_CORE_MODULE ngx_core_module_t 解析全局指令、管理顶层配置 create_conf 与全局变量
事件模块 NGX_EVENT_MODULE ngx_event_module_t 封装 I/O 复用模型 实现 ngx_event_actions_t 的回调
HTTP Handler NGX_HTTP_MODULE ngx_http_module_t 生成/转发 HTTP 响应 实现 content_handler,独占 location
HTTP Filter NGX_HTTP_MODULE ngx_http_module_t 修改响应头/体,链式处理 插入 ngx_http_top_body_filter
HTTP Upstream NGX_HTTP_MODULE ngx_http_module_t 自定义负载均衡算法 实现 ngx_http_upstream_peer_t
Mail 模块 NGX_MAIL_MODULE ngx_mail_module_t 邮件协议代理 实现 ngx_mail_protocol_t
Stream 模块 NGX_STREAM_MODULE ngx_stream_module_t TCP/UDP 四层代理 实现 handler/ filter,类似 HTTP 但操作 session

关键区别

  • 核心模块事件模块属于底层支撑,直接与框架和操作系统交互,不直接处理应用层数据。
  • HTTP 模块是应用开发的主体,但内部 Handler、Filter、Upstream 三者的角色不可混淆:Handler 独占 location 产生响应,Filter 可叠加修改输出,Upstream 定义后端集群策略。
  • Mail 模块Stream 模块分别用于邮件和四层代理,开发范式与 HTTP 模块高度相似,只是上下文和模块类型常量不同。
  • 开发时务必正确填写 ngx_module_t 中的 type 字段,它决定了模块的初始化顺序和可用的钩子函数。

8. 结语

掌握 Nginx 模块的分类与开发套路,能让你从“配置使用”跃迁到“定制扩展”。实际开发中,HTTP Handler 和 Filter 是最常见的二次开发入口,而 Upstream、Stream 则适用于需要自定义负载均衡或四层协议处理的场景。本文提供的代码骨架均为经过精简的最小可运行示例,你可以在此基础上参照 Nginx 现有模块(如 ngx_http_proxy_modulengx_http_gzip_module)进行扩展,快速上手 Nginx 模块化开发。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐