文章目录

  • Nginx
  •  Nginx的进程结构
  •  Nginx进程管理
  •   方式1:信号
  •   方式2:命令行
  •  worker进程处理请求
  •  额外补充
  •   热部署
  • 结束语


Nginx

  在讲Nginx的进程管理之前,先来了解Nginx的进程结构。

   运行已经配置好的Nginx,在Linux命令行中输入以下命令

ps -ef | grep nginx

   找到nginx进程,查看所有关于它的进程详情,如图所示:



wwindows查看nginx进程 nginx 查看进程_子进程


   从结果来看,可以看到,Nginx启动后,会有一个master进程以及一个worker进程。实际上,这个worker进程的数量是可修改的。

   找到nginx.conf文件,如图所示:



wwindows查看nginx进程 nginx 查看进程_nginx_02


   可以看到在文件的头部有一个worker_processes,通过其后面的参数来指定有多少个worker进程。一般来说,这个值设为1就足够了。但是如果有存在SSL或者gzip这些比较消耗CPU的工作的时候,就设置为和CPU的核数一致即可,比如说两个4核CPU,worker_processes就可设为8,甚至可以设为16。

   在这里将worker进程数改为2,重启nginx,再根据ps命令查看nginx进程,结果如下所示:



wwindows查看nginx进程 nginx 查看进程_wwindows查看nginx进程_03


   从相关的进程信息来看,不难发现,Nginx是以一个多进程的方式来工作的。事实上,Nginx也存在单进程的方式,但单进程结构并不适用于生产环境,只适用于个人开发调试使用。

   那么接下来来看一下多进程的Nginx中的进程模型,如图所示:



wwindows查看nginx进程 nginx 查看进程_Nginx_04


   从图中可知,Nginx在启动以后,会有一个父进程master和很多个子进程。而子进程又分为两类,一类为工作进程worker,一类为和Cache相关的进程。对于master进程来说,其主要是用于管理worker进程的。换句话说,则是监控每个worker进程的运行状态。对于请求的处理都是通过子进程worker处理,就算是使用的缓存也依旧是用worker进程来处理。

   那么既然说到master进程的作用是管理worker进程,那么就得知道master进程是如何管理worker进程的,如图所示:



wwindows查看nginx进程 nginx 查看进程_wwindows查看nginx进程_05


   从图中可知,进程间的通讯是通过信号来控制的。也就是说,我们可以直接通过给master进程发送信号来操作nginx。而nginx0.8版本以后,就引入了一系列的命令行参数来方便我们操作nginx。

   在了解完Nginx的进程结构以后,我们再来了解Nginx的进程管理。

   所谓的信号方式实际上是通过linux内置的信号来操作Nginx。

   在前面的时候说过,master进程需要去管理worker进程。而master进程去管理worker进程的方式其实是通过监控worker进程有无发送CHLD信号,如果发现worker进程有发送CHLD信号,master进程就会去重新拉起一个worker进程。

   所谓的CHLD信号就是操作系统中规定当子进程终止的时候就会向父进程发送CHLD信号,默认这个信号是被忽略的,若有需要就会让父进程去捕获该信号。

   而我们人为操作Nginx则是通过linux内置的kill命令去传输信号。在这里稍微介绍一下kill命令的格式,如下所示:

kill [-s 信号声明 | -n 信号编号 | -信号声明] 进程号 | 任务声明 … 或 kill -l [信号声明]

   当我们在命令行中输入kill -l命令时会出现以下结果:



wwindows查看nginx进程 nginx 查看进程_nginx_06


   这就是信号声明,前面的数字就是信号编号。特别注意的是,编号1 ~ 31号的信号为传统UNIX支持的信号,是非实时信号。而32 ~ 64的信号是扩充信号,是实时信号,实际上就是差别在于信号会不会丢失的问题。对于信号说明的解释自行查网,这里不过多解释。

   在了解完kill的格式后,接下来查看该Nginx的相关进程信息,如下所示:



wwindows查看nginx进程 nginx 查看进程_Nginx_07


   接下来我们在访问第一章配置的静态资源页面的过程中,向master进程发送SIGTERM信号(程序结束信号),结果如下所示:



wwindows查看nginx进程 nginx 查看进程_子进程_08


   接着我们做同样的操作,向master进程发送SIGQUIT信号,结果如下所示:



wwindows查看nginx进程 nginx 查看进程_子进程_09


   实际上,SIGTERM、SIGINT信号都是快速退出。而SIGQUIT信号则是代表着缓慢退出亦或者称为优雅退出。为什么Nginx支持这个QUIT信号呢,是因为假如在处理一个连接的时候,暂且不论该连接对于请求是扮演怎样的角色,我们将该连接直接关闭,那有可能会存在客户端收到错误的响应的情况。

   因此所谓的缓慢退出就是指worker进程可以识别出当前没有处理请求的连接,然后把这个连接关闭。但是实际上Nginx这个功能并不能针对所有类型的请求,比如说nginx代理webSocket协议。因为webSocket是一个长连接,反向代理服务器需要保持连接持续打开,而不是在空闲时就关闭。

   那么接下来我们再来了解一下SIGHUP信号(该信号默认是终止进程),在命令行中输入kill指令,结果如图所示:



wwindows查看nginx进程 nginx 查看进程_子进程_10


   从结果我们会发现,Nginx进程并没有被关闭,反而发现工作进程的进程号发生了变动。换句话来说,原先的worker进程被关闭了,又新启了一个worker进程。为什么会出现这样的情况呢?

   这是因为nginx启动以后,在系统中就会以Daemon即守护线程的模式在后台运行。而SIGHUP信号有一个例外,就是接收该信号的程序是以守护进程的方式在运行,那么这个信号实际上是通知该程序去重新读取配置文件

   那么又有一个问题了,为什么重新读取配置文件,会导致worker进程发生了变动呢?

   实际上,当我们发送该信号的时候,可能会发现Nginx的worker进程数量增加了,这是因为原先读取的那个配置文件所运行的Nginx的worker进程没有退出的原因。这里再做同样的事情,向master进程发送SIGHUP信号,结果如图所示:



wwindows查看nginx进程 nginx 查看进程_wwindows查看nginx进程_11


   接下来我们就来捋一捋重载配置文件的一个流程,如图所示:



wwindows查看nginx进程 nginx 查看进程_Nginx_12


   相应的解释如下:

    ①外界向master进程发送HUP信号
    ②master进程收到HUP信号,检验配置文件语法是否正确。
    ③在配置文件语法正确的情况下,如果存在新加入的端口则打开新的监听端口。
    ④master进程根据新的配置文件启动新的worker子进程。
    ⑤在启动完新的子进程后,master进程再向旧配置启动的worker子进程发送QUIT信号。
    ⑥旧配置启动的worker子进程收到QUIT信号以后,先关闭监听句柄(保证该进程不会处理新的连接),然后再等处理完当前连接后将自己结束。

   从上面的图以及解释来看,不难发现,Nginx可以在不停止运行的情况下,保持原有的连接处理,更新配置文件,也就是我们往往常说的平滑升级配置文件。而为了保证这个平滑升级,必须要先启动新的worker进程,再结束旧的worker进程

   以上就是通过信号的方式来管理Nginx,接下来再来看看命令行的方式。

   Nginx命令行常用的大约有8种,如下所示:

#帮助:
		./nginx -? || ./nginx -h
	#使用指定的配置文件:
		./nginx -c file
	#指定配置指令:
		./nginx -g 
	#指定运行目录:
		./nginx -p
	#发送信号:
		./nginx -s
		#立刻停止服务:
			./nginx -s stop
		#缓慢停止服务:
			./nginx -s quit
		#重载配置文件:
			./nginx -s reload
		#重新开始记录日志文件:
			./nginx -s reopen
	#测试配置文件是否有语法错误:
		./nginx -t
	#打印nginx的版本信息、编译信息等:
		./nginx -v

   其中,指定配置指令的意思是在Nginx的conf目录存在多条指令,如果想要在命令行中覆盖目录中的指令,则可以使用-g来实现;指定运行目录的意思是会把conf中定义的运行目录替换掉。

   实际上,命令行模式就是向master进程发送信号。也就是说更加方便开发人员去管理Nginx。那么这里就有一个问题了,在上面我们发送信号给程序,除却命令以及参数以外,还需要有一个进程号,而这个进程号又是哪里来的呢?

   在Nginx启动之后,Nginx会将自己的主进程号pid记录到一个名为nginx.pid文件里,而这个文件保存在Nginx安装目录下的logs目录下。找到该文件,通过cat指令查看,如下所示:



wwindows查看nginx进程 nginx 查看进程_子进程_13


   也就是说,当我们使用Nginx命令行的时候,它就会去读取pid文件中的master进程的pid,然后再通过向这个pid所属的进程发送相应的信号,达到管理Nginx的作用。

   最后来总结一下对Nginx进程管理的信号和命令,如下所示:

信号

含义

TERM, INT

立即退出Nginx,等同于./nginx -s stop

QUIT

缓慢退出Nginx,等同于./nginx -s quit

HUP

重载配置文件,等同于./nginx -s reload

USR1

重新打开日志文件,等同于./nginx -s reopen

USR2

切换nginx可执行程序

WINCH

缓慢停止所有的worker进程

  了解完了Nginx的进程模型以及Nginx进程管理的内部处理方式以后,我们再来了解worker进程是怎么处理请求的。

   在上一章的时候稍微了解了内部请求处理的流程,接下来我们再来看看工作进程是如何处理每一个请求的。

   首先对于Nginx的master进程来说,其先要建立好监听的socket,然后再fork出多个worker子进程,而这每一个子进程都接收了建立好的socket。当有一个连接出现时,所有接收了这个socket的进程都会收到信号通知,但是与此同时有且仅有一个进程能够处理该连接,这也就是所谓的惊群效应。

   这个惊群效应会导致系统频繁地调度进程亦或者线程,会大大减少系统性能。而在nginx中旧版本提供了一个accept_mute解决这个惊群效应问题,从名字来看就可以知道是加在accept的一把共享锁,默认是开启的。

   也就是说,每个worker进程会获取这个共享锁,拿到这个锁的进程就会向自己的epoll中添加accept事件去接收连接,之后就开始分别获取请求、解析请求以及处理请求,最后返回给客户端,断开连接。这就是worker进程的一个完整的处理流程。

   实际上,在Nginx1.11.3版本以后,就已经取消了使用共享锁的方式,而是通过WQ_FLAG_EXCLUSIVE标记来实现:新的连接进入到accept队列时,Linux内核有且仅唤醒一个进程来处理该连接的效果

   既然讲到了信号管理这一部分,就顺便在这里讲一下Nginx的热部署功能。

   所谓的热部署其实就是能够在不停止Nginx运行下,更换其主程序。接下来我们来熟悉一下热部署。

   需要注意的是,原先编译生成的程序文件我们需要对其进行一个备份,如图所示:



wwindows查看nginx进程 nginx 查看进程_子进程_14


   接下来,我们将编译好的新版本的程序文件copy到sbin中(这里已经先下载好了新的nginx),如图所示:



wwindows查看nginx进程 nginx 查看进程_Nginx_15


   需要注意的是:新版本的linux系统中,如果想要覆盖一个正在使用的文件时,需要加上-f参数。并且还需要注意的是:编译的Nginx程序文件所指定的相应的配置选项需要保持和旧的Nginx的配置选项是一样的。

   然后,我们需要向现在正在运行的Nginx的master进程发送一个信号,告诉其我们要准备进行热部署。也就是如下命令:

kill -USR2 pid

   接下来,我们查看Nginx的相关进程信息,如下所示:



wwindows查看nginx进程 nginx 查看进程_nginx_16


   从图中我们可得知,现在正在运行着两个master进程,而新的master也会生成新的worker子进程,并且以后新的请求都只会进入到新版本的Nginx的worker子进程中处理。那么接下来我们就需要退出原有的worker进程,就得向旧版本的master进程发送一个winch信号,结果如图所示:



wwindows查看nginx进程 nginx 查看进程_子进程_17


   除去旧版本的master进程以外,剩下的进程都是新版本的Nginx的。可是为什么会留下一个旧版本的master进程呢?这其实是保证发现问题时能够将版本回滚的一个措施。

   回滚使用的命令流程以及结果如下所示:



wwindows查看nginx进程 nginx 查看进程_nginx_18


   这就是一个完整的热部署操作流程。

结束语

'''
		在这一章主要讲了Nginx的进程模型以及相应的管理方式
		顺便在这里提到了Nginx的热部署功能
		下一章开始讲Nginx的配置
	'''