8.4 Nginx启动时框架的处理流程
通过阅读8.3节,读者应该对ngx_cycle_t结构体有了基本的了解,下面继续介绍Nginx在启动时框架做了些什么。注意,本节描述的Nginx启动流程基本上不包含Nginx模块在启动流程中所做的工作,仅仅是展示框架代码如何使服务运行起来,这里的框架主要就是调用表8-2中列出的方法。
如图8-6所示,这里包括Nginx框架在启动阶段执行的所有基本流程,零碎的工作这里不涉及,对一些复杂的业务也仅做简单说明(如图8-6中的第2步涉及的平滑升级的问题),本节关注的重点只是Nginx的正常启动流程。
图 8-6 Nginx启动过程的流程图
下面简要介绍一下图8-6中的主要步骤:
1)在Nginx启动时,首先会解析命令行,处理各种参数。因为Nginx是以配置文件作为核心提供服务的,所以最主要的就是确定配置文件nginx.conf的路径。这里会预先创建一个临时的ngx_cycle_t类型变量,用它的成员存储配置文件路径(实际上还会使用这个临时ngx_cycle_t结构体的其他成员,如log成员会指向屏幕输出日志),最后调用表8-2中的ngx_process_options方法来设置配置文件路径等参数。
2)图8-6中的第2步实际上就是在调用表8-2中的ngx_add_inherited_sockets方法。Nginx在不重启服务升级时,也就是我们说过的平滑升级(参见1.9节)时,它会不重启master进程而启动新版本的Nginx程序。这样,旧版本的master进程会通过execve系统调用来启动新版本的master进程(先fork出子进程再调用exec来运行新程序),这时旧版本的master进程必须要通过一种方式告诉新版本的master进程这是在平滑升级,并且传递一些必要的信息。Nginx是通过环境变量来传递这些信息的,新版本的master进程通过ngx_add_inherited_sockets方法由环境变量里读取平滑升级信息,并对旧版本Nginx服务监听的句柄做继承处理。
3)第3步~第8步,都是在ngx_init_cycle方法中执行的。在初始化ngx_cycle_t中的所有容器后,会为读取、解析配置文件做准备工作。因为每个模块都必须有相应的数据结构来存储配置文件中的各配置项,创建这些数据结构的工作都需要在这一步进行。Nginx框架只关心NGX_CORE_MODULE核心模块,这也是为了降低框架的复杂度。这里将会调用所有核心模块的create_conf方法(也只有核心模块才有这个方法),这意味着需要所有的核心模块开始构造用于存储配置项的结构体。其他非核心模块怎么办呢?其实很简单。这些模块大都从属于一个核心模块,如每个HTTP模块都由ngx_http_module管理(如图8-2所示),这样ngx_http_module在解析自己感兴趣的"http"配置项时,将会调用所有HTTP模块约定的方法来创建存储配置项的结构体(如第4章中介绍过的xxx_create_main_conf、xxx_create_srv_conf、xxx_create_loc_conf方法)。
4)调用配置模块提供的解析配置项方法。遍历nginx.conf中的所有配置项,对于任一个配置项,将会检查所有核心模块以找出对它感兴趣的模块,并调用该模块在ngx_command_t结构体中定义的配置项处理方法。这个流程可以参考图4-1。
5)调用所有NGX_CORE_MODULE核心模块的init_conf方法。这一步骤的目的在于让所有核心模块在解析完配置项后可以做综合性处理。
6)在之前核心模块的init_conf或者create_conf方法中,可能已经有些模块(如缓存模块)在ngx_cycle_t结构体中的pathes动态数组和open_files链表中添加了需要打开的文件或者目录,本步骤将会创建不存在的目录,并把相应的文件打开。同时,ngx_cycle_t结构体的shared_memory链表中将会开始初始化用于进程间通信的共享内存。
7)之前第4步在解析配置项时,所有的模块都已经解析出自己需要监听的端口,如HTTP模块已经在解析http{……}配置项时得到它要监听的端口,并添加到listening数组中了。这一步骤就是按照listening数组中的每一个ngx_listening_t元素设置socket句柄并监听端口(实际上,这一步骤的主要工作就是调用表8-2中的ngx_open_listening_sockets方法)。
8)在这个阶段将会调用所有模块的init_module方法。接下来将会根据配置的Nginx运行模式决定如何工作。
9)如果nginx.conf中配置为单进程工作模式,这时将会调用ngx_single_process_cycle方法进入单进程工作模式。
10)调用所有模块的init_process方法。单进程工作模式的启动工作至此全部完成,将进入正常的工作模式,也就是8.5节和8.6节分别介绍的worker进程工作循环、master进程工作循环的结合体。
11)如果进入master、worker工作模式,在启动worker子进程、cache manage子进程、cache loader子进程后,就开始进入8.6节提到的工作状态,至此,master进程启动流程执行完毕。
12)由master进程按照配置文件中worker进程的数目,启动这些子进程(也就是调用表8-2中的ngx_start_worker_processes方法)。
13)调用所有模块的init_process方法。worker进程的启动工作至此全部完成,接下来将进入正常的循环处理事件流程,也就是8.5节中介绍的worker进程工作循环的ngx_worker_process_cycle方法。
14)在这一步骤中,由master进程根据之前各模块的初始化情况来决定是否启动cache manage子进程,也就是根据ngx_cycle_t中存储路径的动态数组pathes中是否有某个路径的manage标志位打开来决定是否启动cache manage子进程。如果有任何1个路径的manage标志位为1,则启动cache manage子进程。
15)与第14步相同,如果有任何1个路径的loader标志位为1,则启动cache loader子进程。对于第14步和第15步而言,都是与文件缓存模块密切相关的,但本章不会详述。
16)关闭只有worker进程才需要监听的端口。
在以上16个步骤中,简要地列举出了Nginx在单进程模式和master工作方式下的启动流程,这里仅列举出与Nginx框架密切相关的步骤,并未涉及具体的模块。