AIGC标识 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 内部持有 readwrite 事件指针,指向对应数组中的元素。这种设计避免了每个连接单独分配事件结构。

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中的默认值;
    }
}

合并顺序:

  1. ngx_http_merge_main_conf() — 合并main级配置
  2. ngx_http_merge_srv_conf() — 合并server级配置
  3. 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位检测

思考题

  1. 为什么NGINX选择多进程而非多线程?
  2. ngx_cycle_t在配置重载时如何实现无缝切换?
  3. 如果要写一个自定义HTTP模块,需要实现哪些回调函数?
  4. 事件模块和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

无缝切换的关键

  1. 新旧cycle共存:新worker使用新cycle,旧worker使用旧cycle,切换期间两者同时运行
  2. 监听socket共享:新旧worker共享监听socket(通过fork继承),新worker可以立即接受新连接
  3. 旧连接不中断:旧worker继续处理已建立的连接,直到连接关闭
  4. 配置独立:新配置只影响新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模块的配置在每次请求时使用
posted @ 2026-09-04 17:26  IcarusLee  阅读(2)  评论(0)    收藏  举报