Nginx master工作流程

master工作流程图


这里写图片描述

      master进程不需要处理网络事件,它不负责业务的执行,只会通过管理worker等子进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。
      它会通过检查一下7个标志位来决定ngx_master_process_cycle函数的运行(和worker检查4个标志位是差不多的):
1. sig_atomic ngx_reap;
2. sig_atomic ngx_terminate;
3. sig_atomic ngx_quit;
4. sig_atomic ngx_reconfigure;
5. sig_atomic ngx_reopen;
6. sig_atomic ngx_change_binary;
7. sig_atomic ngx_noaccept;
8. ngx_uint_t ngx_restart; //这个不是信号

信号的意义

信号 对应进程中的全局标志变量 意义
QUIT ngx_quit 优雅地关闭整个服务
TERM或者INT ngx_terminate 强制关闭整个服务
USR1 ngx_reopen 重新打开服务中的所有文件
WINCH ngx_noaccept 所有子进程不再接受处理新的连接,相当于对所有子进程发送QUIT信号
USR2 ngx_change_binary 平滑升级到新版本Nginx程序
HUP ngx_reconfigure 重新读取配置文件是服务对新配置项生效
CHILD ngx_reap 有子进程意外结束,这时需要监控所有的子进程,也就是ngx_reap_children做的工作

ngx_processes全局数组

      在了解master的工作流程之前,先了解一下管理子进程的数据结构:ngx_processes全局数组中的元素ngx_process_t。

//定义1024个元素的ngx_processes数组,即最多只能有1024个子进程
#define NGX_MAX_PROCESSES   1024

//当前操作的进程在ngx_processes数组中的下标
ngx_int_t   ngx_process_slot;

//ngx_processes数组中有意义的ngx_process_t元素中最大的下标
ngx_int_t   ngx_last_process;

//存储所有子进程的数组
ngx_process_t   ngx_processes[NGX_MAX_PROCESSES];

typedef struct {
    //进程ID
    ngx_pid_t       pid;
    //由waitpid系统调用获取的子进程状态
    int             status;
    /* 用于master和worker之间通信的socket */
    ngx_socket_t    channel[2];
    //子进程的循环执行方法,当父进程调用ngx_spawn_process生成子进程时使用
    ngx_spawn_proc_pt   proc;
    /* 上面的方法第二个参数需要传递一个指针,它是可选的(因为分为worker、cache manager等)。
    这个data一般与第二个参数等价 */
    void            *data;
    //进程名。
    char            *name;
    //1:重新生成子进程
    unsigned        respawn:1;
    //1:正在生成子进程
    unsigned        just_spawn:1;
    //1:正在进行父进程、子进程分离
    unsigned        detached:1;
    //1:进程正在退出
    unsigned        exiting:1;
    //1:进程已退出
    unsigned        exited:1;
}   ngx_process_t;

master启动一个子进程

      ngx_spawn_process函数封装了fork()系统调用。它会从ngx_processes中选择一个还未使用的ngx_process_t元素,存储这个子进程的相关信息。错误将返回NGX_INVALID_PID。

//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);

      上述的proc方法就是要填入ngx_process_t中,子进程中将要执行的工作循环:

typedef void (*ngx_spawn_proc_pt)(ngx_cycle_t *cycle,void *data);

      worker进程、cache manage进程、cache loader进程都是依照该方法来定义的。

master工作流程

      事实上,master通过ngx_processes数组中的有效ngx_process_t元素内容来判断一个子进程的状态的,而信息的传递是通过Linux内核发来的CHILD信号。此时处理信号的ngx_signal_handler函数会将sig_reap置位,调用ngx_process_get_status函数来修改ngx_processes数组中所有子进程的状态。

实际的工作流程(循环判断8个标志位):
1. 检查ngx_reap标志位。0:没有子进程意外退出,进入下一步;1:利用ngx_reap_children遍历ngx_processes数组,对于非正常退出的子进程重新拉起。如果所有子进程都正常退出,返回一个live=0,否则live=1;
2. 如果live=0:所有子进程已经退出、ngx_terminate=1或者ngx_quit=1,都会调用ngx_master_process_exit函数退出master进程,否则跳到第6步。若live=1,表用还有子进程存在,进入第6步;
3. 调用ngx_master_process_exit函数,其中调用所有模块的exit_master函数;
4. 调用ngx_close_listening_sockets关闭进程中打开的监听端口;
5. 销毁内存池,退出master;
6. 如果ngx_terminate=1,则向所有子进程发送信号TERM,通知子进程强制退出,接下来直接跳到第1步,等待信号激活进程;否则继续下一步;
7. 如果ngx_quit=0,跳到第9步,否则向所有子进程发送QUIT信号,优雅地退出;
8. 发送完QUIT信号后,关闭所有端口,跳到第1步,挂起master进程,等待信号激活;
9. 如果ngx_reconfigure=0,则跳到第13步检查ngx_restart标志位。如果为1,则需要重新读取配置文件。Nginx会初始化新的ngx_cycle_t结构体,用它读取新的配置文件,重新拉起新的worker进程,销毁旧的。
10. 接着调用ngx_start_process方法在拉起一批worker进程,这些worker进程将使用新的ngx_cycle_t结构体。
11. 然后调用ngx_start_cache_manager_processes按照缓存模块的加载情况决定是否拉起新的cache manage或者cache loader进程。这个两个方法调用后一定存在子进程,于是将live置位。
12. 接着11步,向原先的(旧的)所有子进程发送QUIT信号,优雅地退出。
13. 检查ngx_restart标志位。0:跳到15步;1:调用ngx_start_worker_processes拉起worker进程,同时将ngx_restart复位。
14. 接着调用ngx_start_cache_manager_processes根据缓存情况选择是否启动cache manage进程或者cache loader进程,同时将live置位。
15. 检查ngx_reopen标志位。0:跳到17步;1:调用ngx_reopen_files重新打开所有文件,并将ngx_reopen复位。
16. 向所有子进程发送USR1信号,要求子进程都得重新打开所有文件。
17. 检查ngx_change_binary标志位。1:需要平滑升级Nginx,这是将调用ngx_exec_new_binary函数,用新的子进程启动新版本的Nginx程序,同时将ngx_change_binary复位。
18. 检查ngx_noaccept标志位。0:跳到第1步,进入下一个循环;1:向所有子进程发送QUIT信号,要求它们优雅地关闭服务,同时将ngx_noaccept复位,将ngx_noaccpeting置位,表示正在停止接受新的连接。

      master进程并不是busy loop,而是会通过sigsuspend系统调用,让master进程休眠,等待master进程收到信号后激活master进程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章