📋 整体流程概览

┌─────────────────────────────────────────────────────────────────┐ │ Nginx 启动与请求处理流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 【启动阶段】 │ │ 1. main() │ │ ↓ │ │ 2. ngx_init_cycle() ← 初始化周期结构 │ │ ↓ │ │ 3. ngx_conf_init() ← 创建配置解析上下文 │ │ ↓ │ │ 4. ngx_conf_parse() ← 解析配置文件 │ │ ↓ │ │ 5. ngx_conf_post_configuration() ← 配置后处理 │ │ ↓ │ │ 6. 创建监听 socket,fork worker 进程 │ │ │ │ 【请求处理阶段】 │ │ 1. ngx_worker_process_cycle() │ │ ↓ │ │ 2. ngx_event_loop() ← 事件循环 │ │ ↓ │ │ 3. ngx_http_process_request_line() ← 读取请求行 │ │ ↓ │ │ 4. ngx_http_process_request_headers() ← 读取请求头 │ │ ↓ │ │ 5. ngx_http_core_run_phases() ← 执行 HTTP 处理阶段 │ │ ↓ │ │ 6. 根据配置中的 location 匹配,调用相应 handler │ │ │ └─────────────────────────────────────────────────────────────────┘

一、配置文件加载到内存结构

1.1 核心数据结构

Nginx 使用 ngx_cycle_tngx_conf_t 两个核心结构体来管理配置:

结构体 作用 关键字段
ngx_cycle_t Nginx 运行周期结构,保存全局状态 conf_ctx - 所有模块的配置上下文
pool - 内存池
connections - 连接数组
listening - 监听端口数组
ngx_conf_t 配置解析上下文 cycle - 指向当前 cycle
conf_file - 当前解析的文件
handler - 配置项处理函数
ctx - 模块配置上下文

1.2 配置解析入口 - ngx_conf_parse()

这是配置解析的核心函数,位于 src/core/ngx_conf_file.c

/* 
 * 配置文件解析主函数
 * cf: 配置解析上下文
 * filename: 配置文件路径 (可选,NULL 表示使用默认)
 */
char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
    u_char      *p;
    char        *rv;
    ngx_int_t    rc;
    ngx_buf_t   *buf;
    ngx_str_t    s;
    ngx_conf_file_t *conf_file;

    /* 如果是首次解析,打开配置文件 */
    if (filename) {
        cf->conf_file->file = ngx_open_file(filename->data, ...);
        if (cf->conf_file->file == NGX_INVALID_FILE) {
            return NGX_CONF_ERROR;
        }
    }

    /* 主解析循环:逐行读取配置文件 */
    for ( ;; ) {
        /* 读取一行配置 */
        rc = ngx_conf_read_token(cf);

        if (rc == NGX_ERROR) {
            return NGX_CONF_ERROR;
        }

        /* 解析完成 */
        if (rc == NGX_DONE) {
            return NGX_CONF_OK;
        }

        /* 处理解析到的指令 */
        rc = ngx_conf_handler(cf, last);

        if (rc == NGX_ERROR) {
            return NGX_CONF_ERROR;
        }
    }
}
关键点:配置解析是逐行进行的,每行被解析为 token 序列,然后调用对应的 handler 函数处理。

1.3 配置项处理 - ngx_conf_handler()

该函数负责查找并调用对应指令的处理函数:

static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
    char           *rv;
    void           *conf, **confp;
    ngx_uint_t      i, found;
    ngx_cmd_t      *cmd;

    found = 0;

    /* 遍历所有模块的命令,查找匹配的指令 */
    for (i = 0; cf->cycle->modules[i]; i++) {
        cmd = cf->cycle->modules[i]->commands;
        if (cmd == NULL) {
            continue;
        }

        for ( ; cmd->name.len; cmd++) {
            /* 比较指令名称 */
            if (cf->args->elts[0].len != cmd->name.len) {
                continue;
            }

            if (ngx_strcmp(cf->args->elts[0].data, cmd->name.data) != 0) {
                continue;
            }

            found = 1;
            break;
        }

        if (found) {
            break;
        }
    }

    /* 调用找到的命令处理函数 */
    rv = cmd->set(cmd, conf, confp, cf->args);

    return (rv == NGX_CONF_OK) ? NGX_OK : NGX_ERROR;
}
重要:每个配置指令(如 worker_processeslistenroot)都有对应的 set 函数,该函数负责将配置值存储到内存结构体中。

二、配置在内存中的存储结构

2.1 三层配置结构

Nginx HTTP 模块的配置分为三个层级,每个层级对应不同的结构体:

层级 结构体 作用域 示例指令
Main ngx_http_core_main_conf_t 全局配置 worker_processes, user
Srv ngx_http_core_srv_conf_t server 块配置 server_name, listen
Loc ngx_http_core_loc_conf_t location 块配置 root, proxy_pass
┌─────────────────────────────────────────────────────────────┐ │ 配置内存结构层次图 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ngx_http_core_main_conf_t (全局) │ │ ├─ servers: array<ngx_http_core_srv_conf_t> │ │ ├─ phase_engine: 处理阶段引擎 │ │ └─ ... │ │ │ │ └─ ngx_http_core_srv_conf_t (server 块) │ │ ├─ server_names: array<server_name> │ │ ├─ locations: array<ngx_http_core_loc_conf_t> │ │ ├─ listen: 监听端口配置 │ │ └─ ... │ │ │ │ └─ ngx_http_core_loc_conf_t (location 块) │ │ ├─ root: 根目录路径 │ │ ├─ proxy_pass: 代理地址 │ │ ├─ handler: 请求处理函数指针 │ │ └─ ... │ │ │ └─────────────────────────────────────────────────────────────┘

2.2 location 配置示例 - ngx_http_core_location()

/* 
 * 处理 location 块配置
 * 当解析到 location /xxx { ... } 时调用
 */
static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_cmd_t *cmd, void *conf)
{
    ngx_http_core_srv_conf_t  *cscf;
    ngx_http_core_loc_conf_t  *clcf, *alcf;

    cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);

    /* 创建 location 配置结构 */
    clcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t));
    
    /* 解析 location 修饰符和路径 */
    if (ngx_strcmp(value[1].data, "=") == 0) {
        clcf->name = value[2];
        clcf->exact_match = 1;
    } else if (ngx_strcmp(value[1].data, "~") == 0) {
        clcf->name = value[2];
        clcf->regex = 1;
    } else {
        clcf->name = value[1];
    }

    /* 将 location 添加到 server 的 location 列表 */
    if (cscf->locations == NULL) {
        cscf->locations = ngx_array_create(cf->pool, 1, 
                                           sizeof(ngx_http_core_loc_conf_t *));
    }
    
    clcfp = ngx_array_push(cscf->locations);
    *clcfp = clcf;

    /* 递归解析 location 块内的配置 */
    rv = ngx_conf_parse(cf, NULL);

    return rv;
}

三、请求处理时配置如何起作用

3.1 请求处理的 11 个阶段

HTTP 请求处理分为 11 个阶段,每个阶段都有对应的 handler,这些 handler 来自配置:

阶段 处理函数 典型用途
1. Post Read ngx_http_core_post_read_phase 请求读取后处理
2. Server Rewrite ngx_http_core_rewrite_phase server 块重写
3. Find Config ngx_http_core_find_config_phase 查找匹配的 location
4. Rewrite ngx_http_core_rewrite_phase location 块重写
5. Post Rewrite ngx_http_core_post_rewrite_phase 重写后处理
6. Pre Access ngx_http_core_pre_access_phase 访问控制前处理
7. Access ngx_http_core_access_phase 访问权限检查
8. Post Access ngx_http_core_post_access_phase 访问控制后处理
9. Try Files ngx_http_core_try_files_phase try_files 指令处理
10. Content ngx_http_core_content_phase 生成响应内容
11. Log ngx_http_core_log_phase 记录访问日志

3.2 关键:查找匹配的 location

在 Find Config 阶段,Nginx 根据请求 URI 查找匹配的 location 配置:

/* 
 * 查找配置 - 第 3 阶段
 * 根据请求 URI 找到对应的 location 配置
 */
static void
ngx_http_core_find_config_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph)
{
    u_char                    *p;
    size_t                     len;
    ngx_http_core_loc_conf_t  *clcf;

    r->content_handler = NULL;
    r->uri_changed = 0;

    /* 核心:根据 URI 查找匹配的 location */
    clcf = ngx_http_core_find_location(r);

    r->loc_conf = clcf->loc_conf;

    /* 检查请求方法是否允许 */
    if ((r->method & clcf->limit_except) == 0) {
        r->main->count++;
        ngx_http_internal_redirect(r, clcf->name, NULL);
        return;
    }

    /* 设置内容处理 handler */
    r->content_handler = clcf->handler;

    ph++;
    r->phase_handler = ph - r->phase_engine.handlers;
    r->write_event_handler(r);
}
关键点:ngx_http_core_find_location() 函数会遍历 server 块下的所有 location,根据匹配规则(精确匹配、前缀匹配、正则匹配)找到最合适的 location 配置。

3.3 内容生成阶段 - 配置中的 handler 如何被调用

/* 
 * 内容生成阶段 - 第 10 阶段
 * 调用 location 配置中设置的 handler 生成响应
 */
static void
ngx_http_core_content_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph)
{
    size_t     root;
    ngx_int_t  rc;
    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    /* 如果 location 设置了 handler,直接调用 */
    if (r->content_handler) {
        r->write_event_handler = ngx_http_request_empty_handler;
        rc = r->content_handler(r);
        return;
    }

    /* 没有 handler 时,尝试作为静态文件处理 */
    if (clcf->root.len == 0) {
        ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
        return;
    }

    /* 拼接文件路径并返回文件内容 */
    rc = ngx_http_map_uri_to_path(r, &path, &root, 0);
    if (rc == NGX_ERROR) {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    ngx_http_finalize_request(r, ngx_http_static_handler(r));
}
handler 的来源:不同的模块会注册不同的 handler。例如:
  • proxy_passngx_http_proxy_handler
  • fastcgi_passngx_http_fastcgi_handler
  • returnngx_http_return_handler
  • 无特殊指令 → 静态文件处理

四、完整示例:从配置到代码执行

4.1 配置示例

# nginx.conf
http {
    server {
        listen 80;
        server_name example.com;
        
        # location 1: 静态文件
        location /static/ {
            root /var/www;
        }
        
        # location 2: 反向代理
        location /api/ {
            proxy_pass http://backend:8080;
        }
        
        # location 3: 精确匹配
        location = /health {
            return 200 "OK";
        }
    }
}
┌─────────────────────────────────────────────────────────────────┐ │ 配置解析与内存结构对应关系 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 配置文件 内存结构 │ │ ┌──────────────┐ ┌─────────────────────────────┐ │ │ │ server { │──────────▶│ ngx_http_core_srv_conf_t │ │ │ │ listen 80; │ │ ├─ port = 80 │ │ │ │ ... │ │ ├─ server_name = ... │ │ │ │ │ │ └─ locations[] = [...] │ │ │ │ location │ │ │ │ │ │ │ /static/ { │──────────▶│ ├─[0]─▶ clcf: │ │ │ │ root │ │ ├─ name = "/static/" │ │ │ /var/www; │ │ ├─ root = "/var/www" │ │ │ } │ │ └─ handler = static │ │ │ │ │ │ │ │ │ │ location │ │ ├─[1]─▶ clcf: │ │ │ │ /api/ { │──────────▶│ ├─ name = "/api/" │ │ │ proxy_pass │ │ └─ handler = proxy │ │ │ ...; │ │ │ │ │ │ │ } │ │ └─[2]─▶ clcf: │ │ │ │ │ │ ├─ name = "/health" │ │ │ location │ │ ├─ exact_match = 1 │ │ │ = /health { │──────────▶│ └─ handler = return │ │ │ return 200 │ │ │ │ │ │ "OK"; │ └─────────────────────────────┘ │ │ │ } │ │ │ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘

4.2 请求处理流程示例

当请求 GET /api/users 到达时:

步骤 函数 操作
1 ngx_http_process_request_line() 解析请求行,得到 GET /api/users HTTP/1.1
2 ngx_http_process_request_headers() 解析请求头
3 ngx_http_core_find_config_phase() 查找匹配的 location → 找到 /api/
4 ngx_http_core_access_phase() 执行访问控制检查
5 ngx_http_core_content_phase() 调用 proxy_handler 处理请求
6 ngx_http_proxy_handler() 向后端 http://backend:8080/api/users 转发请求

五、关键源码文件索引

文件路径 作用 关键函数
src/core/ngx_conf_file.c 配置文件解析核心 ngx_conf_parse(), ngx_conf_handler()
src/core/ngx_cycle.c 运行周期管理 ngx_init_cycle()
src/http/ngx_http_core_module.c HTTP 核心模块 ngx_http_core_location(), ngx_http_core_find_location()
src/http/ngx_http_request.c 请求处理 ngx_http_process_request(), ngx_http_core_run_phases()
src/http/ngx_http_core_module.h HTTP 核心结构定义 ngx_http_core_loc_conf_t, ngx_http_phase_t

💡 学习建议

  • 阅读源码时,建议从 ngx_conf_parse() 入手,理解配置解析流程
  • 重点关注配置值如何存储到结构体中,以及在请求处理时如何被读取使用
  • 使用 GDB 或打印日志跟踪关键函数的执行路径
  • 结合 Nginx 源码中的注释(英文注释通常更详细)一起阅读