详解nginx及FPM平滑重启

平滑重启

GR是Graceful Restart(平滑重启)的简称,是一种在协议重启时保证转发业务不中断的机制。 GR机制的核心在于:当某设备进行协议重启时,能够通知其周边设备在一定时间内将到该设备的邻居关系和路由保持稳定。在协议重启完毕后,周边设备协助其进行信息(包括支持GR的路由/MPLS相关协议所维护的各种拓扑、路由和会话信息)同步,在尽量短的时间内使该设备恢复到重启前的状态。在整个协议重启过程中不会产生路由振荡,报文转发路径也没有任何改变,整个系统可以不间断地转发数据。这个过程即称为平滑重启。

nginx平滑重启

nginx进程分为master主进程和worker工作进程,nginx的平滑重启通过信号HUB控制。

hup.png | center | 827x338

注:在POSIX兼容的平台上,SIGUSR1和SIGUSR2是发送给一个进程的信号,它表示了用户定义的情况。

为了详细分析nginx的平滑重启过程,我们持续监控nginx进程变化。 发送HUP信号

kill -HUP `cat /home/git/nginx/logs/nginx.pid`

nginx_1.png | center | 827x126

nginx_2.png | center | 827x136

nginx_3.png | center | 827x83

通过观察,可以分析出大致的平滑重启过程为: 1. master使用新配置 fork出n-1个worker及新master 2. 新worker处理新情求,旧worker执行完退出 3. master重新加载配置,期间使用新master接管服务 4. master加载配置完毕,新master切换为worker工作模式 平滑重启完,master进程号并不会发生变化。

nginx平滑升级

HUP仅用于平滑重启,加载配置等,如果要平滑升级nginx版本,重新加载编译的二进制文件,需要借助于USR2信号。 1. 发送USR2信号

kill -USR2 `cat /home/git/nginx/logs/nginx.pid`

nginx_4.png | center | 827x132

pid.png | center | 665x37

观察到nginx进程,fork出新master及worker,此时nginx.pid内容已经发生变化,并且在logs目录下生成了nginx.pid.oldbin文件,记录旧master pid. 2. 向旧master发送WINCH信号,nginx woker会优雅地停止服务,即:停止接收新的请求,但是不会终止已经在处理的请求。一段时间后,旧nginx的所有worker进程全部退出,只剩下master进程,而用户请求全部都由新的nginx进程处理。

kill -WINCH `cat /home/git/nginx/logs/nginx.pid.oldbin`

nginx_5.png | center | 827x83

  1. 向旧master发送QUIT信号,旧nginx进程完全退出,至此平滑升级完成。
kill -QUIT `cat /home/git/nginx/logs/nginx.pid.oldbin`

nginx_6.png | center | 827x62

FPM平滑重启

FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,php5.3.3之后已经集成FPM,在./configure的时候带 –enable-fpm参数即可开启PHP-FPM。 FPM的平滑重启需要通过USR2信号控制,不过与nginx的平滑重启过程有较大的不同。

kill -USR2 `cat /home/git/php/var/run/php-fpm.pid`

fpm_1.png | center | 827x275

通过持续观察fpm进程可以看到,FPM平滑重启,需要等子进程完全退出后,才会启动新的master及子进程,随后旧master退出。 使用strace进一步分析

strace_2.png | center | 827x186

发现master通知所有子进程退出,包含正在处理请求的子进程。 为了进一步验证这个结论,编写一个服务端sleep脚本

<?php
exec("sleep 5");
echo 'done';

用浏览器请求这个地址,并在此期间平滑重启fpm,请求直接502了。 nginx错误日志:

[error] 29841#0: *1646 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "GET /test.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9001", host: "localhost"

php bug#60961,也有对fpm无法优雅的实现平滑重启的说明。 难道FPM这么low?答案当时是no,实际上通过 process_control_timeout 参数可以实现我们的目标。

process_control_timeout 设置子进程接受主进程复用信号的超时时间。可用单位:s(秒),m(分),h(小时)或者 d(天)。默认单位:s(秒)。默认值:0(关闭)。 原则上,php-fpm会选择空闲的fastcgi进程去处理请求,在处理之前,php-fpm会给fastcgi发送信号,用来让fastcgi进程准备好接受请求处理。但是fastcgi进程并不总是能够处理请求,也就是不能总是响应该信号(比如出现假死的情况),这时候就需要设定php-fpm留给fastcgi进程响应信号的时间,如果超时了,php-fpm会想其他办法(例如选择其他fastcgi进程),这个就是process_control_timeout参数的作用。 这个参数缺省是 0,也就是不生效,修改为10,重新验证,502已经不会再出现。 结论:缺省情况下,PHP-FPM 无法保证平滑的执行 reload 操作,必须设置一个合理的 process_control_timeout 才行,同时需要注意的是其值不能设置的过大,否则系统可能出现严重的请求堵塞问题。

详解nginx及FPM平滑重启》上有1条评论

melody—lql进行回复 取消回复

电子邮件地址不会被公开。 必填项已用*标注