nginx进程模型
nginx采用一个master管理进程、多个worker工作进程(还有可选的cache相关进程)的设计方式:
启动nginx后(多进程启动。nginx也可以单进程启动),我们可以观察到系统中的进程状态如下图(配置文件中设置worker进程的个数为1):
其中,master进程主要用来管理worker进程,包括接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。而基本的网络事件,则是放在worker进程中来处理了。
下面,我们就来具体了解一下worker进程和master进程的工作机制。
worker进程工作流程
1)
worker进程中循环执行ngx_worker_process_cycle方法来控制进程的运行,它会关注以下4个全局标志位:
sig_atomic_t ngx_terminate;
sig_atomic_t ngx_quit;
ngx_uint_t ngx_exiting;
sig_atomic_t ngx_reopen;
其中的ngx_terminate、ngx_quit、ngx_reopen标志位都将由worker进程中处理信号的方法ngx_signal_handler根据其接收到的信号来设置。
// ngx_signal_handler方法原型
void ngx_signal_handler(int signo);
2)
ngx_worker_process_cycle方法会通过检查ngx_exiting、ngx_terminate、ngx_quit、ngx_reopen这4个标志位来决定后续动作:
文件名:ngx_process_cycle.c
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
...
// 循环
for(; ;) {
// 如果ngx_exiting为1,进程退出
if(ngx_exiting) {
...
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
// ngx_exiting不为1或还有事件要处理则调用ngx_process_events_and_timers方法处理事件
ngx_process_events_and_timers(cycle);
// 如果ngx_terminate为1,强制结束进程
if(ngx_terminate) {
...
}
// 如果ngx_quit为1,优雅地关闭进程
if(ngx_quit) {
...
}
// 如果ngx_reopen为1,重新打开所有文件
if(ngx_reopen) {
...
}
}
}
具体的工作流程如下图:
master进程工作流程
1)
与worker进程的工作机制类似,master进程会通过检查以下7个标志位来决定ngx_master_process_cycle方法的执行:
sig_atomic_t ngx_reap;
sig_atomic_t ngx_terminate;
sig_atomic_t ngx_quit;
sig_atomic_t ngx_reconfigure;
sig_atomic_t ngx_reopen;
sig_atomic_t ngx_change_binary;
sig_atomic_t ngx_noaccept;
ngx_signal_handler方法也会根据接收到的信号设置以上这些标志位。
除此之外,还有一个与信号无关的标志位也在master的工作流程中使用到:ngx_uint_t ngx_restart;
2)
在分析master进程的工作流程前,我们需要了解master进程管理子进程的数据结构——ngx_process全局数组。定义如下:
// 定义1024个元素的ngx_process数组,也就是最多只能有1024个子进程
#define NGX_MAX_PROCESS 1024
// 当前操作的进程在ngx_process数组中的下标
ngx_int_t ngx_process_slot;
// ngx_process数组中有意义的ngx_process_t元素中最大的下标
ngx_int_t ngx_last_process;
// 存储所有子进程的数组
// 所有子进程相关的状态信息都保存在ngx_process数组中
ngx_process_t ngx_process[NGX_MAX_PROCESS];
// 数组元素类型ngx_process_t结构体的定义
typedef struct {
ngx_pid_t pid; // 进程ID
int status; // 父进程由waitpid系统调用获取到的进程状态
// 这是由socketpair系统调用产生出的用于进程间通信的socket句柄
// 这一对socket句柄可以互相通信,目前用于master父进程与worker子进程问的通信
ngx_socket_t channel[2];
// 子进程的循环执行方法,当父进程调用ngx_spawn_process生成子进程时使用
ngx_spawn_proc_pt proc;
// 上面的ngx_spawn_proc_pt方法中第2个参数雷要传递1个指针,它是可选的
// 例如,worker子进程就不需要,而cache manage进程就需要ngx_cache_manager_ctx上下文成员
// 这时,data一般与ngx_spawn_proc_pt方法中第2个参数是等价的
void *data;
char *name; // 进程名称。操作系统中显示的进程名称与name相同
unsigned respawn:1; // 标志位,为1时表示在重新生成子进程
unsigned just_spawn:1; // 标志位,为1时表示正在生成子进程
unsigned detached:1; // 标志位,为1时表示在进行父、子进程分离
unsigned exiting:1; // 标志位,为1时表示进程正在退出
unsigned exited:1; // 标志位,为1时表示进程已经退出
} ngx_process_t;
3)
master进程通过ngx_spawn_process方法启动一个子进程,其中封装了fork系统调用,并且会从ngx_process数组中选择一个还未使用的ngx_process_t元素来存储这个子进程的相关信息(如果所有1024个数组元素中已经没有空余的元素,即子进程个数超过了最大值1024,那么将会返回NGX_INVALID_PID)。因此,ngx_process数组中元素的初始化在ngx_spawn_process方法中进行。
// ngx_spawn_process方法的定义
// 参数中的函数指针proc是指子进程中将要执行的工作循环
ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn);
// ngx_spawn_proc_pt的定义
typedef void(*ngx_spawn_proc_pt)(ngx_cycle_t *cycle, void *data);
// worker进程、cache manage/loader进程的工作循环也是依照ngx_spawn_proc_pt来定义的
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data);
static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data);
4)
ngx_master_process_cycle方法的逻辑如下图:
master进程的工作循环如下图:
注意:此工作循环并不是在不停地执行以上步骤,而是会通过sigsuspend调用使master进程休眠,等待master进程收到信号后激活master进程继续由上面的第一步执行循环。
5) master进程对进程的控制有以下两种方式:
I.手动发送信号
master进程接收信号从而管理众worker进程,那么,可以通过kill调用向master进程发送信号,比如kill -s SIGHUP <nginx master pid>用以通知nginx重读配置项并重启:master进程在接收到信号后,会先重新加载配置,然后再启动新进程开始接收新请求,并向所有老进程发送信号告知不再接收新请求并在处理完所有未处理完的请求后自动退出。
II.自动发送信号
可以通过带命令行参数启动新进程来发送信号给master进程,比如./nginx -s reload用以启动一个新的nginx进程,而新进程在解析到reload参数后会向master进程发送信号(新进程会帮我们把手动发送信号中的动作自动完成)。
小结
nginx的这种设计具有以下几个优点:
1)利用多核系统的并发处理能力
现代操作系统已经支持多核CPU架构,这使得多个进程可以占用不同的CPU核心来工作。如果只有一个进程在处理请求,则必然会造成CPU资源的浪费。如果多个进程间的地位不平等,则必然会有某一级同一地位的进程成为瓶颈,因此,nginx中所有的worker工作进程都是完全平等的(woker进程个数一般设置为机器CPU核数)。这提高了网络性能,降低了请求的时延。
2)负载均衡
多个worker工作进程间通过进程间通信来实现负载均衡,也就是说,一个请求到来时,会优先分配到负载较轻的worker进程中处理。这将降低请求的时延,并在一定程度上提高网络性能。
3) 管理进程会负责监控工作进程的状态,并负责管理其行为
管理进程不会占用多少系统资源,它只是用来启动、停止、监控或使用其他行为来控制工作进程。首先,这提高了系统的可靠性,当工作进程出现问题时,管理进程可以启动新的工作进程来避免性能的下降。其次,管理进程支持nginx服务运行中的程序升级、配置项的修改等操作。这种设计使得动态可扩展性、动态定制性、动态可进化性较容易实现。
参考资料:
陶辉.深入理解Nginx 模块开发与架构解析.北京:机械工业出版社,2013
nginx进程模型. http://www.cnblogs.com/liushaodong/archive/2013/02/26/2933511.html
nginx平台初探. http://tengine.taobao.org/book/chapter_02.html#id1