9. Nginx 模块开发:模块类型区别与实例代码详解
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_processes、error_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. 事件模块开发范式
事件模块负责封装 epoll、kqueue 等系统调用,向上层提供统一的异步事件接口。开发事件模块时,需要实现 ngx_event_module_t 上下文,并提供 ngx_event_actions_t 结构体中的回调函数(如 add、del、process_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 模块需要完成以下步骤:
- 定义模块指令表(
ngx_command_t),可在配置中设置参数。 - 实现 Handler 函数,负责填充
ngx_http_request_t并返回响应。 - 在
ngx_http_module_t上下文的postconfiguration或preconfiguration中,将 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 接口中的 get、free、notify 等函数,并在配置解析时将自定义 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_module、ngx_http_gzip_module)进行扩展,快速上手 Nginx 模块化开发。
更多推荐




所有评论(0)