nginx-源码带读-01-架构概览
NGINX 源码带读 第1篇:架构概览
本篇目标
建立NGINX的全局架构视角,理解源码目录结构、核心数据结构、配置系统和模块系统。深入源码层面理解NGINX的启动流程和核心机制。
前置知识
- C语言基础
- HTTP协议基础
- Linux系统编程(进程、信号、socket)
1. NGINX是什么
NGINX是一个高性能的Web服务器和反向代理。核心特点:
- 事件驱动:非阻塞I/O + epoll,单进程处理数万并发连接
- 模块化:功能通过模块组合实现
- Master-Worker模型:1个master进程 + N个worker进程
- 配置重载:不中断服务更新配置
- 内存高效:使用内存池和Slab分配器,减少系统调用
1.1 架构全景
客户端请求
|
v
+-- NGINX ------------------------------------------+
| |
| master进程 |
| |-- fork --> worker-1 (epoll) ----+ |
| |-- fork --> worker-2 (epoll) ----+--> 后端服务 |
| |-- fork --> worker-3 (epoll) ----+ |
| |
| 共享内存区 |
| |-- accept_mutex (自旋锁) |
| |-- connection_counter (连接计数) |
| |-- ngx_temp_number (临时变量) |
| |-- STAT_STUB (统计信息) |
| |
| 配置系统 |
| |-- main_conf (全局) |
| |-- srv_conf (服务器) |
| |-- loc_conf (位置) |
+-----------------------------------------------------+
1.2 NGINX版本信息
NGINX源码中定义了版本号:
// src/core/nginx.h
#define nginx_version 1031005
#define NGINX_VERSION "1.31.5"
#define NGINX_VER "nginx/" NGINX_VERSION
2. 源码目录结构
nginx-source/src/
├── core/ 核心基础:字符串、数组、日志、内存池、哈希、红黑树
├── event/ 事件驱动:epoll/kqueue/select/poll
│ └── modules/ 平台事件模块
├── http/ HTTP子系统:请求处理、过滤链、upstream
│ └── modules/ HTTP模块(proxy、fastcgi、gzip等)
│ └── v2/ HTTP/2
├── mail/ 邮件代理模块
├── stream/ TCP/UDP流代理模块
├── os/ 平台相关代码
│ └── unix/ Linux实现(epoll、进程管理)
│ └── win32/ Windows实现
└── misc/ 杂项工具
2.1 src/core/ 核心文件
| 文件 | 行数 | 职责 |
|---|---|---|
ngx_string.c/h |
~1000 | 字符串操作(ngx_str_t,非null结尾) |
ngx_palloc.c/h |
~430 | 内存池分配器:bump-pointer + 大块链表 |
ngx_hash.c/h |
~800 | 哈希表(支持通配符匹配) |
ngx_rbtree.c/h |
~600 | 红黑树(定时器核心) |
ngx_buf.c/h |
~200 | 缓冲区(ngx_buf_t) |
ngx_chain.c/h |
~100 | 缓冲区链表 |
ngx_connection.c/h |
~1657 | 连接结构体和连接池管理 |
ngx_cycle.c/h |
~1484 | 运行周期核心结构 |
ngx_conf_file.c/h |
~1800 | 配置文件解析 |
ngx_module.c/h |
~360 | 模块系统 |
ngx_log.c/h |
~600 | 日志系统 |
ngx_list.c/h |
~150 | 链表 |
ngx_array.c/h |
~300 | 动态数组 |
ngx_queue.c/h |
~200 | 队列 |
ngx_slab.c/h |
~817 | Slab分配器(共享内存) |
ngx_spinlock.c/h |
~100 | 自旋锁 |
ngx_regex.c/h |
~500 | PCRE正则封装 |
2.2 src/event/ 事件系统
| 文件 | 行数 | 职责 |
|---|---|---|
ngx_event.c/h |
~1373 | 事件核心:ngx_process_events_and_timers |
ngx_event_accept.c |
~350 | 接受新连接 |
ngx_event_connect.c |
~300 | 发起连接 |
modules/ngx_epoll_module.c |
~1051 | Linux epoll实现 |
modules/ngx_kqueue_module.c |
~800 | macOS kqueue实现 |
modules/ngx_select_module.c |
~300 | select备选实现 |
2.3 src/http/ HTTP子系统
| 文件 | 行数 | 职责 |
|---|---|---|
ngx_http.c |
~2200 | HTTP模块初始化 |
ngx_http_request.c |
~4235 | 请求处理核心 |
ngx_http_core_module.c |
~5476 | 核心模块:11个阶段 |
ngx_http_upstream.c |
~7350 | 反向代理核心 |
ngx_http_write_filter_module.c |
~500 | 输出过滤链的末端 |
modules/ngx_http_proxy_module.c |
~3000 | proxy_pass模块 |
3. ngx_cycle_t —— 运行周期核心
ngx_cycle_t 是NGINX最核心的数据结构,每个worker进程持有一个cycle实例。它包含了服务器运行所需的全部状态。
struct ngx_cycle_s {
void ****conf_ctx; // 四重指针:模块类型→模块索引→配置结构体
ngx_pool_t *pool; // 内存池
ngx_log_t *log; // 当前日志
ngx_log_t new_log; // 新日志(配置重载时)
ngx_str_t hostname; // 主机名
ngx_uint_t connection_n; // 最大连接数
ngx_connection_t *connections; // 连接池数组
ngx_event_t *read_events; // 读事件数组(与connections一一对应)
ngx_event_t *write_events; // 写事件数组
ngx_uint_t listening_n; // 监听socket数
ngx_listening_t *listening; // 监听socket列表
ngx_uint_t free_connection_n; // 空闲连接数
ngx_connection_t *free_connection; // 空闲连接链表头
ngx_queue_t reusable_connections_queue; // 可复用连接队列
ngx_array_t *open_files; // 打开的文件列表
ngx_array_t *shared_memory; // 共享内存区域列表
ngx_uint_t modules_n; // 模块总数
ngx_module_t **modules; // 模块指针数组
ngx_uint_t modules_used; // 模块是否已初始化
// 配置文件相关
ngx_str_t conf_file; // 配置文件路径
ngx_str_t conf_param; // 配置参数(-g选项)
ngx_str_t conf_prefix; // 配置文件目录
ngx_str_t prefix; // 安装前缀目录
ngx_str_t error_log; // 错误日志路径
ngx_str_t lock_file; // 锁文件路径
struct ngx_cycle_s *old_cycle; // 旧cycle(配置重载时)
};
3.1 conf_ctx 的四重指针
conf_ctx 是理解NGINX配置系统的关键:
void ****conf_ctx;
// 等价于:
// conf_ctx[模块类型索引][模块索引] = 配置结构体指针
// 例如:
// conf_ctx[NGX_CORE_MODULE][0] = ngx_core_module的main_conf
// conf_ctx[NGX_HTTP_MODULE][1] = 第2个HTTP模块的main_conf
配置解析后,每个模块在每个配置层级(main/srv/loc)都有独立的配置结构体。
3.2 三个并行数组
NGINX为每个连接维护三个并行数组:
connections[0] --> read_events[0] --> write_events[0]
connections[1] --> read_events[1] --> write_events[1]
...
connections[N] --> read_events[N] --> write_events[N]
每个 ngx_connection_t 内部持有 read 和 write 事件指针,指向对应数组中的元素。这种设计避免了每个连接单独分配事件结构。
3.3 cycle的生命周期
nginx启动
-> ngx_init_cycle(NULL) 创建初始cycle
-> 解析配置文件
-> 初始化模块
配置重载 (nginx -s reload)
-> master收到SIGHUP
-> ngx_init_cycle(old_cycle) 创建新cycle
-> 比较新旧监听socket(继承fd)
-> 比较共享内存区域(重用或新建)
-> 销毁旧cycle的多余资源
-> fork新的worker进程
-> 旧worker优雅退出
4. 启动流程:从main()到事件循环
4.1 入口:main()
// src/core/nginx.c
int main(int argc, char *const *argv) {
// 1. 解析命令行参数
// 2. 初始化cycle(创建初始cycle)
cycle = ngx_init_cycle(&init_cycle);
// 3. 创建日志文件
ngx_log_set_log(&cycle->new_log, log_fd);
// 4. 启动master进程
ngx_master_process_cycle(cycle);
}
4.2 ngx_master_process_cycle()
// src/os/unix/ngx_process_cycle.c
void ngx_master_process_cycle(ngx_cycle_t *cycle) {
// 1. 设置信号处理器
ngx_init_signals();
// 2. 创建监听socket
ngx_open_listening_sockets(cycle);
// 3. 启动worker进程
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
// 4. 启动缓存管理进程(可选)
if (ccf->master && ccf->worker_rlimit_nofile == 0) {
ngx_start_cache_manager_processes(cycle, 0);
}
// 5. 主循环:等待信号
for ( ;; ) {
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"cycle:%V", &cycle->conf_file);
ngx_memzero(&set, sizeof(sigset_t));
sigemptyset(&set);
sigaddset(&set, SIGALRM);
sigaddset(&set, SIGIO);
sigaddset(&set, SIGCHLD);
// ... 其他信号
sigsuspend(&set); // 等待信号
ngx_time_update();
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"woke up %e", ngx_elapsed);
// 处理信号
if (ngx_accept_mutex) {
// 重新获取accept锁
if (ngx_trylock(&ngx_accept_mutex) == NGX_OK) {
ngx_enable_accept_events(cycle);
}
}
ngx_close_old_listening_sockets(cycle);
ngx_close_old_connections(cycle);
}
}
4.3 worker进程启动
// src/os/unix/ngx_process_cycle.c
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, char *me) {
// 初始化
ngx_worker_process_init(cycle, me);
// 主事件循环
for ( ;; ) {
// 检查退出标志
if (ngx_quit) {
ngx_log_error(..., "worker shutting down");
ngx_close_listening_sockets(cycle);
ngx_close_idle_connections(cycle);
break;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"worker cycle: %V", &cycle->conf_file);
// 核心:事件处理
ngx_process_events_and_timers(cycle);
}
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
// 清理
ngx_worker_process_exit(cycle);
}
4.4 启动流程图
main()
|
+-> ngx_init_cycle() 创建初始cycle
+-> ngx_master_process_cycle()
|
+-> ngx_start_worker_processes(N)
|
+-> fork() --> worker-1
+-> fork() --> worker-2
+-> fork() --> worker-3
|
+-> [master] sigsuspend() 等待信号
|
+-> SIGHUP: 重载配置
+-> SIGUSR1: 重新打开日志
+-> SIGUSR2: 热升级
+-> SIGTERM/SIGQUIT: 退出
5. 配置系统
5.1 三级配置层级
main_conf 全局配置(worker_processes、error_log等)
├── srv_conf 服务器配置(listen、server_name等)
│ └── loc_conf 位置配置(root、proxy_pass等)
5.2 配置结构体定义
每个模块定义自己的配置结构体:
// HTTP模块的loc_conf示例
typedef struct {
ngx_str_t root; // root指令值
ngx_str_t alias; // alias指令值
ngx_array_t *limit_req; // limit_req指令值
ngx_flag_t enable; // 模块开关
ngx_uint_t max_body_size; // 最大请求体大小
ngx_msec_t keepalive; // keepalive超时
} ngx_http_loc_conf_t;
5.3 配置指令表
每个模块定义自己的 ngx_command_t 数组:
static ngx_command_t ngx_http_proxy_commands[] = {
{ ngx_string("proxy_pass"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_proxy_pass, // 解析函数
NGX_HTTP_LOC_CONF_OFFSET, // 存储偏移
0, // 结构体偏移
NULL },
{ ngx_string("proxy_set_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE2,
ngx_http_proxy_set_header,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, headers),
NULL },
// ...
ngx_null_command
};
指令标志位说明:
| 标志 | 含义 |
|---|---|
NGX_HTTP_MAIN_CONF |
可在main块使用 |
NGX_HTTP_SRV_CONF |
可在server块使用 |
NGX_HTTP_LOC_CONF |
可在location块使用 |
NGX_HTTP_LIF_CONF |
可在location块的if中使用 |
NGX_CONF_TAKE1~N |
需要1~N个参数 |
NGX_CONF_FLAG |
布尔开关(on/off) |
NGX_CONF_NOARGS |
无参数 |
NGX_CONF_BLOCK |
需要代码块({}) |
5.4 配置解析流程
nginx.conf
-> ngx_conf_read_token() 逐token读取
-> ngx_conf_handler() 遍历所有模块的命令表
-> 匹配指令名
-> 调用对应的解析函数
-> 存储到对应层级的conf结构体
解析完成后:
-> 每个模块调用merge函数
-> 子级配置继承父级配置(子级未设置时使用父级值)
5.5 配置合并机制
配置合并(merge)是NGINX配置系统的核心:
// 伪代码:合并srv_conf
for (每个HTTP模块) {
if (子server有该配置) {
使用子server的配置;
} else {
使用父级main_conf中的默认值;
}
}
合并顺序:
ngx_http_merge_main_conf()— 合并main级配置ngx_http_merge_srv_conf()— 合并server级配置ngx_http_merge_loc_conf()— 合并location级配置(递归)
6. 模块系统
6.1 模块类型
NGINX有8种模块类型:
| 类型 | 说明 | 数量 |
|---|---|---|
NGX_CORE_MODULE |
核心模块 | ~10 |
NGX_HTTP_MODULE |
HTTP模块 | ~100+ |
NGX_MAIL_MODULE |
邮件模块 | ~5 |
NGX_STREAM_MODULE |
流代理模块 | ~20 |
NGX_EVENT_MODULE |
事件模块 | ~10 |
NGX_OPENTELEMETRY_MODULE |
可观测性模块 | ~3 |
NGX_Dynamic_MODULE |
动态加载模块 | 动态 |
NGX_RAW_CONFIG_MODULE |
原始配置模块 | 动态 |
6.2 模块定义结构
typedef struct ngx_module_s {
ngx_uint_t ctx_index; // 同类型模块内的索引
ngx_uint_t index; // 全局索引
char *name; // 模块名称
ngx_uint_t spare0, spare1;
ngx_uint_t version; // 版本(必须等于nginx_version)
const char *signature; // 编译特征签名(~34个标志位)
void *ctx; // 模块上下文(类型不同结构不同)
ngx_command_t *commands; // 配置命令表
ngx_uint_t type; // 模块类型
ngx_int_t (*init_master)(ngx_log_t *log);
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
uintptr_t spare_hook0..7;
} ngx_module_t;
6.3 签名机制
NGINX使用 signature 字段检测模块二进制兼容性:
// src/core/ngx_module.h
#define NGX_MODULE_SIGNATURE \
NGX_MODULE_SIGNATURE_1 NGX_MODULE_SIGNATURE_2 ...
// 包含约34个编译特征标志:
// 指针大小、off_t大小、time_t大小、哪些特性被编译等
动态模块加载时检查签名匹配,防止不兼容的模块被加载。
6.4 模块加载流程
nginx启动
-> ngx_preinit_modules() // 分配全局index
-> ngx_init_cycle() // 初始化cycle
-> ngx_count_modules() // 计算每种类型的ctx_index
-> ngx_init_modules() // 调用所有模块的init_module钩子
-> ngx_add_module() // 加载动态模块(如果有)
-> 检查版本和签名
-> dlopen() 加载.so文件
-> 调用ngx_module_t定义的初始化函数
6.5 HTTP模块上下文
typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf); // 配置解析前
ngx_int_t (*postconfiguration)(ngx_conf_t *cf); // 配置解析后
void *(*create_main_conf)(ngx_conf_t *cf); // 创建main配置
void *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf); // 创建srv配置
void *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
void *(*create_loc_conf)(ngx_conf_t *cf); // 创建loc配置
void *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
回调时机:
| 回调 | 调用时机 | 用途 |
|---|---|---|
create_*_conf |
ngx_http_block() |
为每个层级创建配置结构体 |
preconfiguration |
解析http{}之前 | 注册变量等 |
postconfiguration |
解析http{}之后 | 注册过滤器、阶段handler |
init_main_conf |
配置合并后 | 初始化main配置默认值 |
7. 编译系统
7.1 configure脚本
auto/ 目录包含configure脚本,负责检测系统环境和选择模块:
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_gzip_static_module \
--add-module=/path/to/custom_module
7.2 模块生成
configure脚本会生成 objs/ngx_modules.c,列出所有编译进NGINX的模块:
// objs/ngx_modules.c(自动生成)
ngx_module_t *ngx_modules[] = {
&ngx_core_module,
&ngx_errlog_module,
&ngx_http_module,
&ngx_http_core_module,
&ngx_http_log_module,
// ...
NULL
};
7.3 动态模块
# 编译动态模块
./configure --add-dynamic-module=/path/to/module
make modules
# 生成 objs/ngx_http_mymodule.so
# 使用动态模块
load_module modules/ngx_http_mymodule.so;
动态模块加载限制:
- 最多128个动态模块(
NGX_MAX_DYNAMIC_MODULES 128) - 必须与主程序签名兼容
8. 信号管理
8.1 关键信号
| 信号 | 作用 | 处理者 |
|---|---|---|
SIGQUIT |
优雅退出 | master |
SIGTERM |
强制停止 | master |
SIGHUP |
重载配置 | master |
SIGUSR1 |
重新打开日志 | master |
SIGUSR2 |
热升级 | master |
SIGCHLD |
子进程退出 | master |
SIGALRM |
定时器 | worker |
8.2 信号处理器注册
// src/os/unix/ngx_process.c
void ngx_init_signals(void) {
struct sigaction sa;
// SIGCHLD:非阻塞处理子进程退出
sa.sa_handler = ngx_process_get_status;
sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWR|SA_RESTART|SA_SIGINFO;
sigaction(SIGCHLD, &sa, NULL);
// SIGHUP:重载配置
sa.sa_handler = ngx_signal_handler;
sigaction(SIGHUP, &sa, NULL);
// SIGUSR1:重新打开日志
sa.sa_handler = ngx_signal_handler;
sigaction(SIGUSR1, &sa, NULL);
// ... 其他信号
}
9. 连接管理
9.1 连接分配
// src/core/ngx_connection.c
ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log) {
ngx_connection_t *c;
// 从空闲链表获取
c = ngx_cycle->free_connections;
if (c == NULL) {
ngx_log_error(NGX_LOG_ALERT, log, 0,
"%ui worker_connections are not enough",
ngx_cycle->connection_n);
return NULL;
}
// 从链表头取出
ngx_cycle->free_connections = c->data;
ngx_cycle->free_connection_n--;
// 注册到files数组(如果使用fd事件)
if (ngx_cycle->files) {
ngx_cycle->files[s] = c;
}
// 保留事件指针,清零其他字段
rev = c->read;
wev = c->write;
ngx_memzero(c, sizeof(ngx_connection_t));
// 翻转instance位(用于检测过期事件)
instance = rev->instance;
ngx_memzero(rev, sizeof(ngx_event_t));
ngx_memzero(wev, sizeof(ngx_event_t));
rev->instance = !instance;
wev->instance = !instance;
c->fd = s;
c->read = rev;
c->write = wev;
c->log = log;
c->number = ngx_atomic_fetch_add(&ngx_connection_counter, 1);
return c;
}
9.2 instance位技巧
NGINX使用 instance 位检测过期事件:
1. 注册事件时:ee.data.ptr = (c | instance)
2. 事件触发时:检查 (c->fd != -1) && (c->read->instance == instance)
3. 如果instance不匹配,说明连接已被复用,忽略此事件
这解决了:旧socket关闭后被新socket复用,导致旧epoll事件被错误处理的问题。
9.3 连接回收
// 空闲连接不足时,回收可复用连接
static void ngx_drain_connections(ngx_cycle_t *cycle) {
// 当空闲连接 < 总连接数/16 时触发
// 回收 min(32, 可复用连接数/8) 个连接
n = ngx_min(cycle->connection_n / 16, 32);
for (i = 0; i < n; i++) {
// 从可复用队列取最老的连接
c = ngx_cycle->reusable_connections_queue;
// 关闭连接
ngx_close_connection(c);
}
}
10. 本篇小结
| 概念 | 要点 |
|---|---|
| 架构 | 事件驱动 + 多进程 + 模块化 |
| 目录 | core/event/http/mail/stream/os |
| cycle | 运行周期核心,每次reload重建 |
| 配置 | 三级层级:main/srv/loc |
| 模块 | 8种类型,通过ctx和commands定义 |
| 编译 | auto/configure脚本 |
| 启动 | main -> ngx_init_cycle -> ngx_master_process_cycle |
| 连接 | 预分配数组 + 空闲链表 + instance位检测 |
思考题
- 为什么NGINX选择多进程而非多线程?
- ngx_cycle_t在配置重载时如何实现无缝切换?
- 如果要写一个自定义HTTP模块,需要实现哪些回调函数?
- 事件模块和HTTP模块的ctx结构有什么区别?
思考题解答
1. 为什么NGINX选择多进程而非多线程?
NGINX选择多进程(master-worker)模型而非多线程,原因如下:
内存隔离:
- 多线程共享进程地址空间,一个线程的内存越界可能破坏其他线程的数据
- 多进程通过fork实现COW(Copy-on-Write),每个worker有独立的地址空间
- 一个worker崩溃不会影响其他worker
避免锁竞争:
- 多线程需要对共享数据加锁,锁竞争是性能杀手
- 多进程通过accept mutex在进程间分发连接,避免了复杂的锁设计
- 每个worker独立处理连接,不需要细粒度的锁
编程简单:
- 单线程事件驱动模型(每个worker一个线程)编程简单,无并发问题
- 不需要考虑线程安全、死锁、竞态条件
- 调试简单——问题通常可以复现
与epoll的配合:
- 每个worker一个epoll实例,独立监听事件
- 通过accept mutex避免多个worker同时accept同一个socket
历史原因:NGINX最初设计时(2004年),Linux的线程支持和NPTL还存在问题。多进程模型在FreeBSD(NGINX的诞生地)上表现更好。
代价:进程间通信比线程间通信复杂,共享内存需要特殊机制(shm)。但对于Web服务器场景,这种代价是值得的。
2. ngx_cycle_t在配置重载时如何实现无缝切换?
配置重载(nginx -s reload)的流程:
master收到SIGHUP
-> 创建新的cycle(新配置)
-> ngx_init_cycle() 解析新配置
-> fork新的worker进程
-> 旧worker收到优雅退出信号
-> 不再接受新连接
-> 处理完当前连接后退出
-> 新worker开始处理新连接
-> master关闭旧worker的监听socket
无缝切换的关键:
- 新旧cycle共存:新worker使用新cycle,旧worker使用旧cycle,切换期间两者同时运行
- 监听socket共享:新旧worker共享监听socket(通过fork继承),新worker可以立即接受新连接
- 旧连接不中断:旧worker继续处理已建立的连接,直到连接关闭
- 配置独立:新配置只影响新worker,旧配置只影响旧worker
// src/core/ngx_cycle.c
ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle) {
// 1. 分配新cycle
cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));
// 2. 复制旧配置文件路径
cycle->conf_file = old_cycle->conf_file;
// 3. 分配配置上下文数组
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
// 4. 解析配置文件
ngx_conf_parse(&conf, &cycle->conf_file);
// 5. 比较新旧监听socket,继承已有的fd
// 6. 销毁旧cycle的多余资源
}
3. 自定义HTTP模块需要实现哪些回调函数?
一个HTTP模块需要实现的5个核心部分:
1. 模块定义(ngx_module_t):
ngx_module_t ngx_http_mymodule = {
NGX_MODULE_V1,
&ngx_http_mymodule_ctx, // 上下文
ngx_http_mymodule_commands, // 命令表
NGX_HTTP_MODULE, // 模块类型
NULL, // init_master(未使用)
ngx_http_mymodule_init, // init_module
ngx_http_mymodule_init_worker, // init_process
NULL, // exit_process
NULL, // exit_master
NGX_MODULE_V1_PADDING
};
2. 模块上下文(ngx_http_module_t):
preconfiguration:配置解析前调用(注册变量等)postconfiguration:配置解析后调用(注册过滤器等)create_main_conf:创建main级配置结构体init_main_conf:初始化main级配置create_srv_conf:创建srv级配置结构体merge_srv_conf:合并srv级配置create_loc_conf:创建loc级配置结构体merge_loc_conf:合并loc级配置
3. 配置命令(ngx_command_t):
- 定义指令名称、参数、存储位置
- 指定handler解析指令值
4. 内容处理函数:
static ngx_int_t ngx_http_mymodule_handler(ngx_http_request_t *r) {
// 设置响应头
// 构建响应体
// 发送响应
}
5. 注册handler:
- 在
postconfiguration中将handler插入到相应阶段 - 或在
content阶段的checker中调用
4. 事件模块和HTTP模块的ctx结构有什么区别?
| 特性 | 事件模块 | HTTP模块 |
|---|---|---|
| 上下文类型 | ngx_event_module_t |
ngx_http_module_t |
| 配置层级 | 事件级(main) | 三级(main/srv/loc) |
| 主要回调 | add_conf/init_conf |
8个回调(见上题) |
| 配置创建 | 无三级创建 | 每级都有create/merge |
事件模块上下文:
typedef struct {
ngx_int_t (*add_conf)(ngx_conf_t *cf, ngx_event_module_t *module);
ngx_int_t (*init_conf)(ngx_cycle_t *cycle, ngx_event_module_t *module);
void *(*create_conf)(ngx_cycle_t *cycle);
void (*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_event_module_t;
HTTP模块上下文:
typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
void *(*create_main_conf)(ngx_conf_t *cf);
void *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf);
void *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
void *(*create_loc_conf)(ngx_conf_t *cf);
void *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
设计差异的原因:
- 事件模块只需要初始化事件机制(epoll/kqueue),配置相对简单
- HTTP模块需要处理复杂的配置继承(server/location嵌套),所以需要三级配置系统
- 事件模块的生命周期与worker进程相同,HTTP模块的配置在每次请求时使用

浙公网安备 33010602011771号