问题 启动或确保在应用程序/服务器重新启动时运行延迟作业


我们必须使用delayed_job(或其他一些后台作业处理器)在后台运行作业,但我们不允许更改服务器上的启动脚本/启动级别。这意味着如果提供程序重新启动服务器,则不保证守护程序仍然可用(因为守护程序将由每个部署仅运行一次的capistrano配方启动)。

目前,我能想到确保delayed_job守护程序始终运行的最佳方法是向我们的Rails应用程序添加一个初始化程序,以检查守护程序是否正在运行。如果它没有运行,那么初始化程序启动守护进程,否则它就会离开它。

因此,问题是我们如何检测到Delayed_Job守护程序是从脚本内部运行的? (我们应该能够相当容易地启动一个守护进程,我不知道如何检测一个守护进程是否已经激活)。

有人有主意吗?

问候, 伯尼

基于下面的答案,这就是我提出的。只需将它放在config / initializers中就可以了:

#config/initializers/delayed_job.rb

DELAYED_JOB_PID_PATH = "#{Rails.root}/tmp/pids/delayed_job.pid"

def start_delayed_job
  Thread.new do 
    `ruby script/delayed_job start`
  end
end

def process_is_dead?
  begin
    pid = File.read(DELAYED_JOB_PID_PATH).strip
    Process.kill(0, pid.to_i)
    false
  rescue
    true
  end
end

if !File.exist?(DELAYED_JOB_PID_PATH) && process_is_dead?
  start_delayed_job
end

7403
2018-04-05 20:06


起源

在你的回答中,我们不应该供应 -e production ? - nathanvda
使用rails3这个解决方案对我不起作用。开始这个过程是完全错误的:它继续开始额外的工作。我回到了capistrano任务:) - nathanvda
对于Rails 4+,您应该替换 script/delayed_job 同 bin/delayed_job 在 - 的里面 start_delayed_job 方法 - Brian Hellekin


答案:


检查是否存在守护进程PID文件(File.exist? ...)。如果它在那里然后假设它正在运行其他启动它。


5
2018-04-05 20:57



大!听起来很简单!你会碰巧找到那个档案吗? - btelles
您将在应用程序的tmp / pids文件夹中找到该文件。您可能还想检查是否存在具有该文件ID的进程。崩溃后,PID文件可能仍然存在。 - Tomas Markauskas
优秀!谢谢!我现在还要投票,等待一天或两天还有其他选择。 - btelles
你必须寻找PID文件 和 如果进程表中存在具有该ID的条目, 和 可能还会检查它是否有正确的名称。前两个测试很可能是真的,如果系统意外停止并留下陈旧的PID文件,那么该ID的进程就是预期的那个。也许更快的测试方法是让守护进程响应某种心跳请求。发送请求,如果得到正确的响应,您就知道您需要的代码正在运行。在这种情况下,不需要PID文件测试。 - the Tin Man


一些更清理的想法:不需要“开始”。你应该拯救“没有这样的过程”,以便在出现其他问题时不要启动新进程。拯救“没有这样的文件或目录”以简化条件。

DELAYED_JOB_PID_PATH = "#{Rails.root}/tmp/pids/delayed_job.pid"

def start_delayed_job
  Thread.new do 
    `ruby script/delayed_job start`
  end
end

def daemon_is_running?
  pid = File.read(DELAYED_JOB_PID_PATH).strip
  Process.kill(0, pid.to_i)
  true
rescue Errno::ENOENT, Errno::ESRCH   # file or process not found
  false
end

start_delayed_job unless daemon_is_running?

请记住,如果您启动多个工作程序,此代码将不起作用。并查看script / delayed_job的“-m”参数,该参数与守护进程一起生成监视进程。


9
2017-12-14 17:20





感谢问题中提供的解决方案(以及启发它的答案:-)),它适用于我,即使有多个工作人员(Rails 3.2.9,Ruby 1.9.3p327)。

它让我担心我可能忘记在对lib进行一些更改之后重新启动delayed_job,导致我在实现之前调试了几个小时。

我把以下内容添加到我的 script/rails 文件是为了允许每次启动rails时执行问题中提供的代码,但不是每次工作者启动时都执行:

puts "cleaning up delayed job pid..."
dj_pid_path = File.expand_path('../../tmp/pids/delayed_job.pid',  __FILE__)
begin
  File.delete(dj_pid_path)
rescue Errno::ENOENT # file does not exist
end
puts "delayed_job ready."

我面临的一个小缺点是,它也被调用了 rails generate 例如。我没有花太多时间寻找解决方案,但欢迎提出建议:-)

请注意,如果您使用的是独角兽,则可能需要添加相同的代码 config/unicorn.rb 之前 before_fork 呼叫。

- 已编辑: 在使用上述解决方案玩了一点之后,我最终做了以下事情:

我创建了一个文件 script/start_delayed_job.rb 内容:

puts "cleaning up delayed job pid..."
dj_pid_path = File.expand_path('../../tmp/pids/delayed_job.pid',  __FILE__)

def kill_delayed(path)
  begin
    pid = File.read(path).strip
    Process.kill(0, pid.to_i)
    false
  rescue
    true
  end
end

kill_delayed(dj_pid_path)

begin
  File.delete(dj_pid_path)
rescue Errno::ENOENT # file does not exist
end

# spawn delayed
env = ARGV[1]
puts "spawing delayed job in the same env: #{env}" 

# edited, next line has been replaced with the following on in order to ensure delayed job is running in the same environment as the one that spawned it
#Process.spawn("ruby script/delayed_job start")
system({ "RAILS_ENV" => env}, "ruby script/delayed_job start")

puts "delayed_job ready."

现在我可以在任何我想要的地方要求这个文件,包括'script / rails'和'config / unicorn.rb':

# in top of script/rails
START_DELAYED_PATH = File.expand_path('../start_delayed_job',  __FILE__)
require "#{START_DELAYED_PATH}"

# in config/unicorn.rb, before before_fork, different expand_path
START_DELAYED_PATH = File.expand_path('../../script/start_delayed_job',  __FILE__)
require "#{START_DELAYED_PATH}"

0
2018-02-22 10:02





不是很好,但有效

免责声明:我说不是很好,因为这会导致定期重启,这对许多人来说是不可取的。只是尝试启动可能会导致问题,因为如果创建了重复的实例,DJ的实现可能会锁定队列。

你可以安排 cron 定期运行以启动相关作业的任务。由于DJ在作业已经运行时将启动命令视为无操作,因此它才有效。这种方法还处理了DJ因主机重启以外的某些原因而死亡的情况。

# crontab example 
0 * * * * /bin/bash -l -c 'cd /var/your-app/releases/20151207224034 && RAILS_ENV=production bundle exec script/delayed_job --queue=default -i=1 restart'

如果你正在使用宝石 whenever 这非常简单。

every 1.hour do
  script "delayed_job --queue=default -i=1 restart"
  script "delayed_job --queue=lowpri -i=2 restart"
end

0
2017-12-11 16:18