使用USR2重启Unicorn - 退出旧主进程

22

发送USR2信号给Unicorn是很棒的 - 它会启动一个新的主进程,使用你的代码副本,并自动捕获任何更改。太好了!我的问题是:如何停止旧的主进程?显然接受的方式是在before_fork中执行:

before_fork do |server,worker|
  old_pid = '/var/www/current/tmp/pids/unicorn.pid.oldbin'
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end
这个问题在于一旦新的主控节点(和工作者)被生成,它们就会关闭旧的主控节点。因此,对站点的任何请求都将等待新的工作者开始处理,通常需要几秒钟时间来加载整个Rails栈。
如果我删除before_fork,那么从客户端的角度来看,所有的请求都能很快地得到响应:我可以重载浏览器,并且每个请求都能迅速得到填充,除了看到我的代码更改之外,没有任何其他指示新的主控节点何时接管请求。但是,旧的主控节点现在仍然存在,直到我手动发送QUIT为止。
据我所知,一旦工作者完成加载并准备好为客户端提供服务,就没有回调函数可用。这确实是我正在寻找的回调函数。我始终可以在Rails本身中创建一个初始化程序,查找旧的主控节点并将其关闭,但只是想想就让我心痛。
一定有方法!

我非常确定 after_fork 是由工作进程在加载完成后调用的,你可能可以把代码放在那里。 - Adrian Macneil
3个回答

16

我部分解决了这个问题:我看到的行为是由于没有使用preload_app true引起的。如果你设置了它,那么整个应用程序将由主进程加载,工作进程很快就会生成。因此,如果第一个工作进程在此时杀死旧主进程,那么它可以立即开始提供请求服务!

如果您无法使用preload_app true,那么最好的方法可能是将旧pid退出的操作移动到Rails初始化器中,以便带起您的应用程序的第一个工作进程可以一旦Rails已经启动并准备好提供请求服务就立即杀死旧的主进程。


8
如果preload_appfalse,则向Unicorn主进程发送HUP信号似乎是更好的选择。
来自http://unicorn.bogomips.org/SIGNALS.html

HUP - 重新加载配置文件并优雅地重启所有工作进程。如果“preload_app”指令为false(默认情况下),则在重启时工作进程也将获取任何应用程序代码更改。


4
如果"preload_app"设置为true,应该使用USR2+QUIT来加载新的代码。在这种情况下,应用程序代码更改将不会生效。请注意,这里不建议使用其他信号或命令。 - astjohn
糟糕,我想说的是“如果preload_app为false”。 - Milan Iliev
1
如果之前是真的,现在已经不再是了:即使preload_app为true,HUP也会重新加载应用程序。 - Kevin
当前文档(http://unicorn.bogomips.org/SIGNALS.html)似乎仍将`preload_app`列为要求。现在是否有所不同?文档是否已过时? - Milan Iliev

3

这是我的before_fork代码块中的内容:

  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end

为了重启我的“独角兽”应用,我有一个Bash脚本,其中有如下方法:
if sig USR2 && sig 0 && oldsig QUIT
then
    n=$TIMEOUT
    while test -s $old_pid && test $n -ge 0
    do
        printf '.' && sleep 1 && n=$(( $n - 1 ))
    done
    echo

    if test $n -lt 0 && test -s $old_pid
    then
        echo >&2 "$old_pid still exists after $TIMEOUT seconds"
        exit 1
    fi
    exit 0
fi
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
$CMD
;;

该 bash 脚本发送 USR2 信号,该信号会 fork 出新的 unicorn 进程并创建旧的 pid。然后,使用旧的 pid 向旧的 unicorn 发送 QUIT 信号。这个过程非常有效,并且是从 Where Unicorns go to die: Watching unicorn workers with monit 中得到的,这是一个很好的资源。

1
USR2没有为我创建oldbin,可能的原因是什么? - sites

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接