Nginx源码分析三 进程启动和流程解析

Nginx 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。Nginx 以其高性能、稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。在分析 Nginx 的进程启动和流程时,我们主要关注以下几个方面:

1. 进程启动

Nginx 启动时,首先进行的是配置文件的解析和参数的初始化。启动流程主要涉及以下几个函数:

  • ngx_init_cycle(): 这个函数用于初始化 Nginx 的运行环境,包括读取和解析配置文件、初始化模块、打开监听端口等。
  • ngx_master_process_cycle(): 在配置和初始化完成后,Nginx 会启动一个 master 进程。这个函数负责管理 worker 进程,包括 fork 新的 worker 进程、监控 worker 进程的状态等。
  • ngx_worker_process_cycle(): 这是 worker 进程的主函数,负责处理实际的请求。每个 worker 进程都在独立的环境中运行,处理来自客户端的请求。

Nginx 的核心功能和性能优化很大程度上依赖于其进程管理和请求处理的设计。下面是对 ngx_init_cycle(), ngx_master_process_cycle(), 和 ngx_worker_process_cycle() 这三个关键函数的详细介绍:

1. ngx_init_cycle()

这个函数主要负责初始化 Nginx 的运行环境,包括配置解析、模块初始化、内存分配等。它是 Nginx 启动流程中的第一步。

ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle) {
    ngx_cycle_t  *cycle;
    ngx_pool_t   *pool;

    pool = ngx_create_pool(16384, old_cycle->log);
    if (pool == NULL) {
        return NULL;
    }

    cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));
    if (cycle == NULL) {
        ngx_destroy_pool(pool);
        return NULL;
    }

    cycle->pool = pool;
    cycle->log = old_cycle->log;
    cycle->old_cycle = old_cycle;

    // 解析配置文件
    if (ngx_conf_parse(cycle, &cycle->conf_file) != NGX_OK) {
        ngx_destroy_cycle_pools(cycle);
        return NULL;
    }

    // 初始化模块
    if (ngx_init_modules(cycle) != NGX_OK) {
        ngx_destroy_cycle_pools(cycle);
        return NULL;
    }

    // 打开监听端口
    if (ngx_open_listening_sockets(cycle) != NGX_OK) {
        ngx_destroy_cycle_pools(cycle);
        return NULL;
    }

    // 初始化完成后的配置处理
    if (ngx_init_cycle_post_configuration(cycle) != NGX_OK) {
        ngx_destroy_cycle_pools(cycle);
        return NULL;
    }

    return cycle;
}

2. ngx_master_process_cycle()

这个函数是 master 进程的主函数。它负责管理 worker 进程的生命周期,包括 fork 新的 worker 进程、监控 worker 进程的状态、处理信号等。

void ngx_master_process_cycle(ngx_cycle_t *cycle) {
    sigset_t          set;
    ngx_int_t         n;
    ngx_pid_t         pid;
    ngx_queue_t       queue;

    // 设置信号掩码
    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigaddset(&set, SIGALRM);
    sigaddset(&set, SIGIO);
    sigprocmask(SIG_BLOCK, &set, NULL);

    // fork worker 进程
    for (n = 0; n < ngx_worker_processes; n++) {
        pid = ngx_spawn_process(cycle, ngx_worker_process_cycle, "worker process");
        if (pid == -1) {
            return;
        }
    }

    for ( ;; ) {
        sigsuspend(&set);

        // 处理信号
        ngx_signal_handler();

        // 检查和重启 worker 进程
        ngx_reap_children(cycle);
    }
}

3. ngx_worker_process_cycle()

这个函数是 worker 进程的主函数,负责实际处理客户端的请求。每个 worker 进程都在独立的环境中运行,处理来自客户端的请求。

void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) {
    ngx_int_t  rc;
    ngx_uint_t i;

    // 初始化进程环境
    ngx_worker_process_init(cycle);

    for ( ;; ) {
        if (ngx_exiting) {
            ngx_worker_process_exit(cycle);
        }

        // 处理事件和定时器
        rc = ngx_process_events_and_timers(cycle);

        if (rc == NGX_ERROR || rc == NGX_DONE) {
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "worker process exiting");
            ngx_worker_process_exit(cycle);
        }

        // 处理新的请求
        ngx_try_to_accept_new_connection(cycle);
    }
}

这三个函数构成了 Nginx 的核心运行机制,并且整理出完整的流程化代码。

2. 请求处理流程

Nginx 的请求处理流程是模块化的,主要涉及以下几个核心函数:

  • ngx_http_read_request_header(): 读取客户端的请求头。
  • ngx_http_process_request_headers(): 处理请求头,解析出请求的具体参数。
  • ngx_http_handler(): 根据请求的类型(如静态文件请求、动态内容请求等)调用相应的处理模块。
  • ngx_http_output_filter(): 输出过滤函数,用于处理响应数据的发送。

Nginx 的 HTTP 处理流程是高度模块化的,主要通过一系列的处理函数来实现。这些函数负责从读取 HTTP 请求头开始,到处理请求头,再到根据请求类型调用相应的处理模块。下面是对 ngx_http_read_request_header(), ngx_http_process_request_headers(), 和 ngx_http_handler() 这三个核心函数的详细介绍:

1. ngx_http_read_request_header()

这个函数负责从客户端读取 HTTP 请求的头部。它主要通过读取 socket 数据来获取请求的原始数据。

ngx_int_t ngx_http_read_request_header(ngx_http_request_t *r) {
    ssize_t n;
    ngx_event_t *rev;

    rev = r->connection->read;
	// 如果字段数据状态接入到EOF,那么就停止request。
    if (rev->eof) {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client prematurely closed connection");
        ngx_http_close_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
        return NGX_ERROR;
    }
	// 接收recv的字段并给出解析。
    n = ngx_recv(r->connection, r->header_in->pos, r->header_in->end - r->header_in->pos);

    if (n == NGX_ERROR || n == 0) {
        ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
        return NGX_ERROR;
    }

    r->header_in->last += n;

    return ngx_http_parse_request_line(r, r->header_in);
}
2. ngx_http_process_request_headers()

一旦请求行被读取和解析,这个函数就会被调用来处理 HTTP 请求头。它解析 HTTP 头部字段,并填充请求结构体中相应的字段。

ngx_int_t ngx_http_process_request_headers(ngx_http_request_t *r) {
    ngx_int_t rc;
    ngx_table_elt_t *h;
   // loop循环遍历的解析器
    for ( ;; ) {
        rc = ngx_http_parse_header_line(r, r->header_in, 1);

        if (rc == NGX_OK) {
            h = ngx_list_push(&r->headers_in.headers);
            if (h == NULL) {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return NGX_ERROR;
            }

            h->hash = r->header_hash;
            h->key.len = r->header_name_end - r->header_name_start;
            h->value.len = r->header_end - r->header_start;
            h->key.data = ngx_pnalloc(r->pool, h->key.len + 1 + h->value.len + 1 + h->key.len);
            h->value.data = h->key.data + h->key.len + 1;
            ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
            h->key.data[h->key.len] = '\0';
            ngx_memcpy(h->value.data, r->header_start, h->value.len);
            h->value.data[h->value.len] = '\0';

            continue;
        }
// 返回状态等
        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
            return NGX_OK;
        }
// 返回特定状态
        if (rc == NGX_ERROR) {
            ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
            return NGX_ERROR;
        }
    }
}
3.ngx_http_handler()

这个函数是请求处理的分发中心。根据请求的类型(如静态文件请求、动态内容请求等),它调用相应的处理模块。

void ngx_http_handler(ngx_http_request_t *r) {
    ngx_http_core_main_conf_t  *cmcf;
    ngx_http_phase_handler_t   *ph;
    ngx_int_t                  rc;
// 介绍
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    ph = cmcf->phase_engine.handlers;

    r->phase_handler = 0;

    while (ph[r->phase_handler].checker) {
        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        if (rc == NGX_OK) {
            return;
        }

        r->phase_handler++;
    }
}

这些函数展示了 Nginx 如何通过高效的代码结构来处理 HTTP 请求,从读取请求到处理请求,每个步骤都被细分到具体的函数中,确保了代码的清晰和模块化。

3. 进程内容

Nginx 使用多进程模型来处理请求,主要包括一个 master 进程和多个 worker 进程:

  • Master 进程:负责管理 worker 进程,包括启动、停止和监控 worker 进程的状态。Master 进程也负责处理配置文件的重新加载和平滑升级。
  • Worker 进程:实际处理客户端请求的进程。每个 worker 进程都独立运行,互不干扰。

4. 更新的函数字段和进程内容

随着 Nginx 的更新,可能会引入新的功能和模块,这些更新可能会影响到进程启动和请求处理流程。例如,引入新的模块可能需要在 ngx_init_cycle() 中添加新的初始化代码,或者在 ngx_http_handler() 中添加新的处理分支。

结论

Nginx 的设计和实现体现了高效和模块化的设计原则。了解其进程启动和请求处理流程对于深入理解其工作原理和进行高级配置调优非常有帮助。随着新版本的发布,Nginx 的内部实现和功能可能会有所变化,因此建议关注官方的更新日志和文档,以便及时了解最新的变化。