控制 nginx

控制 nginx

翻译原文链接: http://nginx.org/en/docs/control.html

阅读需要了解的前提:

  • 要对 nginx 的基本配置有比较清晰的理解;
  • 要对 nginx 的常见关键术语有清晰的认知,比如 全局配置文件、子配置文件,管理进程,工作进程,二进制文件或者叫可执行文件、日志文件等;
  • 要对 nginx 可执行程序命令行选项有较熟的认知,比如 -s 是什么含义,支持的参数可以有,-t 或者 -T 的区别是,-c 是否可以单独使用等等;
  • 要有一定的 linux 操作系统基础,特别是ps、 kill等 命令,常见的信号以及 nginx的配置脚本(比如 SysV init脚本以及Systemd脚本等),logrotate 程序等。(这个相较于 nginx 应用而言,属于最基础的东西);
  • 我们常说的几个信号,我们要能想明白。比如 TERM, QUIT, HUP, USR1, USR2, WINCH。如果对信号理解不了,切勿在生产环境操作升级或者其他与之相关的操作(当然也不是让你理解到源码层面,如果要考虑实现逻辑,这个是底层代码实现的)。

1、全局说明

nginx 可以通过信号来控制(这个是底层源码层实现的)。默认 nginx 的管理进程(master process) 的进程id会被写入 /usr/local/nginx/logs/nginx.pid 中。这个pid文件的名字会在配置的时候变更或者在 nginx.conf 配置文件中使用 pid 指令来修改。nginx 管理进程支持以下信号:

TERM, INT    快速关闭
QUIT         平滑关闭
HUP          变更配置,保持时区的变化(仅仅是对FreeBSD 和 Linux 系统),使用新的配置文件开启新的工作
			 进程,并且平滑关闭旧的工作进程

USR1         重新打开日志文件
USR2         升级一个可执行文件
WINCH        平滑关闭工作进程

用法一般为:

kill -USR1 `cat /var/run/nginx.pid`  #这里的pid配置文件记录的是master process的进程id号;
或者通过 ps aux 或 ps -ef 加上管道找出nginx 管理进程的pid号,假设为 23456
然后执行:
kill -USER1 23456

单个工作进程也能够被信号控制,虽然一般来说不需要这样做。nginx 的工作进程支持的信号如下:

TERM, INT    快速关闭
QUIT   		 平滑关闭
USR1         重新打开日志文件
WINCH        调试异常终止(需要启用 debug_points),一般生产环境不支持

2、变更配置

为了去让nginx去重载或重读配置文件(即我们常说的 reload config),HUP信号应该被发送给 nginx 的管理进程。管理进程首先会去检查语法的有效性,然后尝试去应用新配置,也就是说,去打开日志文件以及打开新的监听套接字。如果失败了,它会回滚变更,并且会继续使用旧配置去工作。如果成功了,它会启动新的工作进程,并且会向旧的工作进程发送消息请求平滑关闭它们。旧的工作进程收到消息后,会关闭监听套接字,并且继续处理之前服务的旧的客户端连接。当所有客户端服务被处理完成后,旧的工作进程会被关闭。

让我们通过示例解释一下。假设nginx运行在FreeBSD,然后查看命令:

ps axw -o pid,ppid,user,%cpu,vsz,wchan,command | egrep '(nginx|PID)'

产生的输出如下:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
33127 33126 nobody   0.0  1380 kqread nginx: worker process (nginx)
33128 33126 nobody   0.0  1364 kqread nginx: worker process (nginx)
33129 33126 nobody   0.0  1364 kqread nginx: worker process (nginx)

如果 HUP 信号被发送给 管理进程,输出变成:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33129 33126 nobody   0.0  1380 kqread nginx: worker process is shutting down (nginx)
33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33135 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)

其中一个旧的PID为33129的工作进程还在继续工作。过一段时间后它也退出了:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33135 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)

PS: 补充

我们在 nginx的service 服务器管理脚本中,看到reload 的配置核心段如下:

killproc -p ${pidfile} ${prog} -HUP

其中 killproc是/etc/init.d/functions 中定义的一个函数。
pidfile 是nginx pid文件,比如: /var/run/nginx.pid,里面记录的是运行 master process的进程id
prog 是 nginx 
HUP是一种信号,上文中有提及。

由于 functions中定义killproc比较长,这里就不列,最终逻辑还是 调用 kill命令。比如:
kill -HUP PID_NUM

3、轮转日志文件

为了去轮转(rotate)日志文件,它们需要首先被重命名。之后 USR1信号应该被发送给 管理进程。管理进程收到信号后,会重新打开所有当前打开的日志文件并且赋予运行工作进程的非特权用户作为它们的用户属主。成功重新打开后,管理进程会关闭所有打开的文件,并且向所有工作进程发送消息,让它们重新打开文件。工作进程也会立即打开新的文件并且关闭旧的文件。因此,旧的文件可以立即被处理,比如压缩等。

PS: 一般我们yum安装的nginx,有一个自带的基于 logrotate 轮询的配置文件。我们可以根据自己的需要去二次修改处理。 可以参考 : /etc/logrotate.d/nginx 或 man logrotate

或者参考:

http://nginx.org/en/docs/syslog.html

或者自己定义脚本基于定时任务 cron 去实现日志的切割轮询处理。

4、在线升级二进制执行文件

为了去升级服务端的可执行文件(二进制文件,比如 nginx),新的可执行文件应该首先替换旧的可执行文件。

PS: 建议先configure,后 make(要与原先的编译一致),最后的make install 千万先别做。可以直接 mv nginx nginx.old 或者 其他方式备份后。然后用新编译的(路径在编译文件目录下的 objs目录下,make 之后有新的可执行文件nginx生成) 可执行文件替换旧的可执行文件。比如cp或者mv都行。

对于yum安装的nginx,一般它服务器脚本(CentOS 6系列,我可以很确定) ,它支持传入一个 upgrade,这是脚本中定义的一个函数,它会完成下问中,我们说的一系列过程(当然,也不能完全相信脚本,你要自己理解逻辑后才能处理),相关配置片段如下:

upgrade() {
    oldbinpidfile=${pidfile}.oldbin

    configtest -q || return
    echo -n $"Starting new master $prog: "
    killproc -p ${pidfile} ${prog} -USR2
    echo

    for i in `/usr/bin/seq $UPGRADEWAITLOOPS`; do
        /bin/sleep $SLEEPSEC
        if [ -f ${oldbinpidfile} -a -f ${pidfile} ]; then
            echo -n $"Graceful shutdown of old $prog: "
            killproc -p ${oldbinpidfile} ${prog} -QUIT
            RETVAL=$?
            echo
            return
        fi
    done

    echo $"Upgrade failed!"
    RETVAL=1
}

默认变量如下:
UPGRADEWAITLOOPS=${UPGRADEWAITLOOPS:-5}  #从命名的含义来看,表示升级等待循环的次数。就是第一次向旧的管理进程发送 USR2信号后,可能有部分旧的工作进程在平滑关闭中,处理未完成的链接。
SLEEPSEC=${SLEEPSEC:-1}

这里留一个疑问,我们手动处理的时候,除了向旧的管理进程发送 USR2 信号外,后面还会向 旧的管理进程发送一个 WINCH 信号,为什么这个脚本中没有呢?

替换新的可执行文件后,然后向 nginx 的管理进程发送 USR2 信号 (比如 : kill -USR2 23127)。管理进程首先会去重命名 pid 文件,后缀是 .oldbin。比如: /usr/local/nginx/logs/nginx.pid.oldbin,然后使用新的可执行文件启动管理进程,紧接着使用新的管理进程去生成新的工作进程,可以看到如下所示:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33135 33126 nobody   0.0  1380 kqread nginx: worker process (nginx)
33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)

之后,所有的工作进程(旧的和新的) 继续去接受请求。如果 WINCH 信号发送给了第一个管理进程(就是旧的) 执行 命令: kill -WINCH 23127 ,旧的管理进程会向所有它之前创建的工作进程(即旧的工作进程)发送消息,请求它们去平滑关闭(所谓平滑关闭,就是要先处理完 收到管理进程告知要平滑关闭之前接收了没有处理完的客户端链接),如下:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33135 33126 nobody   0.0  1380 kqread nginx: worker process is shutting down (nginx)
36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)


#这里可以看到 进程编号为 33126的进程还没有关闭,可能还有请求要处理。这种进程相较新的工作进程很明显,后面有shutting down标志。
生产中,如果你恰好遇到,长连接一直没有关闭,而且超时时间又很长,可能就要你手动分析处理一下。一般来说,就是不建议强制kill。

过一会儿之后,只有新的工作进程将会处理请求:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)

值得注意的是 旧的管理进程并没有关闭监听套接字,如果有需要的话,它可以用于管理重新启动它的工作进程。如果由于一些原因,新的可执行文件并不工作或者不能良好的工作, 可以按照以下步骤处理:

  • 向旧的管理进程发送 HUP 信号,执行命令类似于 kill -HUP 23127。旧的管理进程将会启动新的工作进程,不会去重读配置文件。之后,新的管理进程(就是之前新执行程序启动的管理进程) 将会被平滑关闭,并且可以向 新的进程发送 QUIT 信号退出,假设新的管理进程的进程ID号为 23112,那么命令为 kill -QUIT 23112。

  • 向新的管理进程发送 TERM 信号,执行命令为 kill -TERM 23112。新的管理进程会向它的工作进程发送消息,让它们立即退出。它们(新的管理进程和新的工作进程) 将会立即退出。(如果新的进程由于某些原因没有退出,我们可以考虑向它们发送 KILL 信号,强制让他们退出。比如有个异常的进程 23119因为特殊原因,在收到管理进程的 TERM后还没有退出,我们可以这样做 , kill -KILL 23119)。当新的管理进程退出后,旧的管理进程会自动启动新的工作进程。

如果新的管理进程退出后,旧的管理进程会丢弃进程PID文件的 .oldbin 后缀,简单说就是新的PID文件去掉后,旧的以 .oldbin 后缀命名的文件会被重新还原正常。 比如, /usr/local/nginx/logs/nginx.pid

如果升级过程是成功的,我们应该向旧的管理进程发送 QUIT 信号,执行命令为 kill -QUIT 23127, 仅仅只有新的进程会保持,如下所示:

  PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
36264     1 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值