NGINX系列之进程模型

Posted by Tango on March 20, 2018

在上一节中分析了nginx主流程,在mian函数中完成服务器的配置文件解析以及模块初始化工作后,根据系统设置进入单进程或者多进程模式,本文将分析nginx进程模型。

进程模型简介

在web服务中,随着用户基数增长,技术演进的趋势是提高系统的并发性和稳定性,一种方式是通过扩展机器的个数实现负载均衡,通过多台机器的量变引起质变,提高系统的并发性,显然这种方式资源利用率较低,成本较高;第二种方式则是演进web服务系统架构,提高单台机器的并发性,例如异步、多线程技术,通过单台机器的并发性提高提升系统整体性能。nginx的高性能与其架构设计密不可分,其主要来源于两个方面:

  • 异步非阻塞 关于异步非阻塞不做过多介绍,参考文章 5种网络编程模型 。nginx底层采用epoll事件处理机制,提高单个进程的并发性。
  • 多进程单线程 单线程避免资源争夺的竞争以及上下文切换带来的损耗,多进程提高“机器”个数,提高系统的整体并发性。

从上文中可知,nginx分为单进程模式和多进程模式,单进程模式常常在开发环境调试时候使用,在对外服务时nginx多以多进程方式工作。多进程工作方式中为方便进程的统一管理,系统中分为一个master进程和多个work进程,master进程主要负责信号处理以及work进程的管理,包括接收外界信号、向worker进程发送信号,监控worker进程的运行状态等,不直接对外提供web服务;worker进程则主要对外提供web服务,各个work进程之间相互隔离且相互平等,从而避免进程之间的资源竞争导致的性能损耗,worker进程数目可以设置,一般设置为机器cpu核数(原因是降低进程之间上下文切换带来的损耗)。其进程模型可以用下图表示:

流程伪代码

master主进程

主进程启动以后首先初始化系统相应的信号量标志位,然后根据配置参数(子进程个数、最大连接数等)通过fork复制创建工作进程,worker进程与master进程具有相同的上下文环境,接下来主进程和工作进程进入不同的循环,主进程保存子进程返回的pid写入文件,接着主进程进入信号处理的循环,监听系统接收到的(例如nginx -reload)信号并进行相关的处理。 master进程有如下几种信号:

  • TERM INT 快速停止
  • QUIT 从容关闭
  • HUP 平滑重启

master进程流程伪代码如下:

void ngx_master_process_cycle(ngx_cycle_t *cycle){


	// 1.初始化信号位
    ngx_init_singal_flag();
    // 2.加载配置参数
    ngx_load_conf();
    // 3.fork创建worker进程
    ngx_start_worker_processes();
    // 4.启动缓存管理工作进程
    ngx_start_cache_manager_processes();
    //进入信号处理循环
    while(1){
    
    switch(singal){
	    case delay:
	          ngx_ delay_singal_handler();
	          break;
	    case quit:
	          ngx_quit_singal_handler();
	          break;
		case terminate:
	          ngx_terminate_singal_handler();
	          break;
		case reconfigure:
	          ngx_reconfigure_singal_handler();
	          break;
		.....    
    }
       
}

worker工作进程

通过主进程fork复制出worker进程,worker进程环境变量(监听socket、文件描述符等)都一样,因此各个worker进程完成等同,在相同的socket端口监听,请求到来时,每个worker工作进程都会监听到,但最终只会有一个worker进程会接受并处理,(此处涉及到惊群现象,将在下篇文章介绍)。创建工作进程以后,工作进程进入循环,首先处理退出信号,然后进入事件处理过程ngx_process_events_and_timers(cycle),在进程处理函数中,首先处理定时任务,然后处理读取任务再处理写任务。这里有三个小问题:

  • 为什么要采用这种处理顺序?
  • 定时任务是如何保证基本准确定时的?
  • 事件处理怎么保证一个请求只被一个进程处理的?

上述问题将在下一篇事件处理模型中介绍。 流程伪代码如下:

static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
    // 1.初始化worker进程
    ngx_worker_process_init(cycle, worker);

    while(1){	
		 // 1.处理退出信息
        if (ngx_exiting) {
            if (ngx_event_no_timers_left() == NGX_OK) {
                 ngx_worker_process_exit(cycle);
            }
        }
        // 2.事件处理 将在下一章节介绍
        ngx_process_events_and_timers(cycle);
		 // 3.
        if (ngx_terminate) {
            ngx_worker_process_exit(cycle);
        }

        if (ngx_quit) {
            if (!ngx_exiting) {
                ngx_exiting = 1;
                ngx_set_shutdown_timer(cycle);
                ngx_close_listening_sockets(cycle);
                ngx_close_idle_connections(cycle);
            }
        }

        if (ngx_reopen) {
           ngx_reopen_files(cycle, -1);
        }
    }
}

精简源码分析

多进程模式ngx_master_process_cycle

  • 文件位置:/src/os/unix/ngx_process_cycle.c
void ngx_master_process_cycle(ngx_cycle_t *cycle){
    char              *title;
    u_char            *p;
    size_t             size;
    ngx_int_t          i;
    ngx_uint_t         n, sigio;
    sigset_t           set;
    struct itimerval   itv;
    ngx_uint_t         live;
    ngx_msec_t         delay;
    ngx_listening_t   *ls;
    ngx_core_conf_t   *ccf;

    // 1.配置相应信号标志位
    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigaddset(&set, SIGALRM);
    sigaddset(&set, SIGIO);
    sigaddset(&set, SIGINT);
    sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
    // 2.配置参数
    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    // 3.启动worker进程
    ngx_start_worker_processes(cycle, ccf->worker_processes,
                               NGX_PROCESS_RESPAWN);
    // 4.缓存管理进程
    ngx_start_cache_manager_processes(cycle, 0);

    // 5.master进程进入循环 处理信号
    for ( ;; ) {
        if (delay) {
        
        }

         if (ngx_reap) {
            ngx_reap = 0;
            live = ngx_reap_children(cycle);
        }

        if (!live && (ngx_terminate || ngx_quit)) {
            ngx_master_process_exit(cycle);
        }

        if (ngx_terminate) {
           ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_TERMINATE_SIGNAL));
            }
            continue;
        }

        if (ngx_quit) {
            ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }

        if (ngx_reconfigure) {
            if (ngx_new_binary) {
                ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);
                ngx_start_cache_manager_processes(cycle, 0);
                ngx_noaccepting = 0;
                continue;
            }

            cycle = ngx_init_cycle(cycle);
            
            ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                   ngx_core_module);
            ngx_start_worker_processes(cycle, ccf->worker_processes,
                                       NGX_PROCESS_JUST_RESPAWN);
            ngx_start_cache_manager_processes(cycle, 1);

            /* allow new processes to start */
            ngx_msleep(100);

            live = 1;
            ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }

        if (ngx_restart) {
            ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);
            ngx_start_cache_manager_processes(cycle, 0);
                   }

        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_reopen_files(cycle, ccf->user);
            ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_REOPEN_SIGNAL));
        }

        if (ngx_change_binary) {
            ngx_change_binary = 0;
            ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
        }

        if (ngx_noaccept) {
            ngx_noaccept = 0;
            ngx_noaccepting = 1;
            ngx_signal_worker_processes(cycle,ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }
    }
}

启动worker进程ngx_start_worker_processes

static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
    ngx_int_t      i;
    ngx_channel_t  ch;

    for (i = 0; i < n; i++) {
        ngx_spawn_process(cycle, ngx_worker_process_cycle,(void *) (intptr_t) i, "worker process", type);
        ngx_pass_open_channel(cycle, &ch);
    }
}

启动缓存进程ngx_start_cache_manager_processes

static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn){
    ngx_uint_t       i, manager, loader;
    ngx_path_t     **path;
    ngx_channel_t    ch;

    manager = 0;
    loader = 0;

    path = ngx_cycle->paths.elts;
    for (i = 0; i < ngx_cycle->paths.nelts; i++) {

        if (path[i]->manager) {
            manager = 1;
        }

        if (path[i]->loader) {
            loader = 1;
        }
    }

    if (manager == 0) {
        return;
    }

    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,
                      &ngx_cache_manager_ctx, "cache manager process",
                      respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN);
    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,
                      &ngx_cache_loader_ctx, "cache loader process",
                      respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN);
    ngx_pass_open_channel(cycle, &ch);
}

单进程ngx_single_process_cycle

void ngx_single_process_cycle(ngx_cycle_t *cycle){
    ngx_uint_t  i;

    ngx_set_environment(cycle, NULL) 
    for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->init_process) {
            if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
                /* fatal */
                exit(2);
            }
        }
    }

    for ( ;; ) {
        
        ngx_process_events_and_timers(cycle);
        if (ngx_terminate || ngx_quit) {

            for (i = 0; cycle->modules[i]; i++) {
                if (cycle->modules[i]->exit_process) {
                    cycle->modules[i]->exit_process(cycle);
                }
            }

            ngx_master_process_exit(cycle);
        }

        if (ngx_reconfigure) {
            ngx_reconfigure = 0;
                    cycle = ngx_init_cycle(cycle);
            if (cycle == NULL) {
                cycle = (ngx_cycle_t *) ngx_cycle;
                continue;
            }

            ngx_cycle = cycle;
        }

        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, (ngx_uid_t) -1);
        }
    }
}

工作进程循环ngx_worker_process_cycle

static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
     ngx_worker_process_init(cycle, worker);

    for ( ;; ) {

        if (ngx_exiting) {
            if (ngx_event_no_timers_left() == NGX_OK) {
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
                ngx_worker_process_exit(cycle);
            }
        }
        
        ngx_process_events_and_timers(cycle);

        if (ngx_terminate) {
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
            ngx_worker_process_exit(cycle);
        }

        if (ngx_quit) {
            ngx_quit = 0;
            ngx_setproctitle("worker process is shutting down");

            if (!ngx_exiting) {
                ngx_exiting = 1;
                ngx_set_shutdown_timer(cycle);
                ngx_close_listening_sockets(cycle);
                ngx_close_idle_connections(cycle);
            }
        }

        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, -1);
        }
    }
}


master进程fork子进程ngx_spawn_process

ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,char *name, ngx_int_t respawn)
{
    //创建子进程
    pid = fork();

    switch (pid) {
    case -1:
        ngx_close_channel(ngx_processes[s].channel, cycle->log);
        return NGX_INVALID_PID;
    case 0: //worker进程
        ngx_parent = ngx_pid;
        ngx_pid = ngx_getpid();
        proc(cycle, data);
        break;
    default:
        break;
    }

    ngx_processes[s].pid = pid;
    ngx_processes[s].exited = 0;

    ngx_processes[s].proc = proc;
    ngx_processes[s].data = data;
    ngx_processes[s].name = name;
    ngx_processes[s].exiting = 0;

    switch (respawn) {

    case NGX_PROCESS_NORESPAWN:
        ngx_processes[s].respawn = 0;
        ngx_processes[s].just_spawn = 0;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_JUST_SPAWN:
        ngx_processes[s].respawn = 0;
        ngx_processes[s].just_spawn = 1;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_RESPAWN:
        ngx_processes[s].respawn = 1;
        ngx_processes[s].just_spawn = 0;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_JUST_RESPAWN:
        ngx_processes[s].respawn = 1;
        ngx_processes[s].just_spawn = 1;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_DETACHED:
        ngx_processes[s].respawn = 0;
        ngx_processes[s].just_spawn = 0;
        ngx_processes[s].detached = 1;
        break;
    }

    if (s == ngx_last_process) {
        ngx_last_process++;
    }

    return pid;
}

在完成工作进程和主进程创建工作以后,系统进入事件处理阶段,其功能实现主要在函数ngx_process_events_and_timers 中,文章介绍事件处理模型。