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

12
我们必须使用delayed_job(或其他后台作业处理器)在后台运行作业,但我们不允许更改服务器上的启动脚本/引导级别。这意味着如果提供商重新启动服务器,则无法保证守护进程仍然可用(因为它将由仅在部署时运行一次的capistrano食谱启动)。
目前,我能想到的最好方法是通过在Rails应用程序中添加一个初始化程序来确保delayed_job守护进程始终运行。如果它没有运行,那么初始化程序会启动守护进程,否则就让它保持不变。
因此,问题是如何从脚本内部检测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

在你的回答中,我们不应该也提供“-e production”吗? - nathanvda
使用Rails3,这个解决方案对我不起作用。启动过程完全出错:它一直在启动额外的任务。我回到了Capistrano任务 :) - nathanvda
对于Rails 4+,您应该在“start_delayed_job”方法内使用“bin/delayed_job”替换“script/delayed_job”。 - Brian Hellekin
4个回答

9

以下是一些更多的清理建议:不需要使用"begin"关键字。你应该捕获 "no such process" 错误,以防止在出现其他问题时启动新进程。同样,也应该捕获 "no such file or directory" 错误以简化条件。

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”参数,该参数将在守护程序(s)与监视器进程一起生成。


5
检查守护进程的PID文件是否存在(File.exist?...)。如果存在,则假定它正在运行,否则启动它。

太好了!听起来很简单!你知道我可以在哪里找到那个文件吗? - btelles
1
你可以在应用程序的 tmp/pids 文件夹中找到该文件。你可能还想检查一下是否存在一个与文件中ID相对应的进程。即使发生崩溃,PID文件仍然可能存在。 - Tomas Markauskas
太好了!谢谢!我会现在点赞,并等待一两天看看是否还有其他选择。 - btelles
3
你需要查找PID文件,如果进程表中存在该ID的条目,并且可能还要检查它是否具有正确的名称。很可能在系统意外崩溃留下过时的PID文件的情况下,前两个测试都是正确的,但该ID处的进程并非所期望的进程。也许更快速的测试方法是让守护进程响应某种心跳请求。发送请求,如果收到正确的响应,则知道所需代码正在运行。在这种情况下,PID文件测试实际上不是必需的。 - the Tin Man

0

不是很好,但能用

免责声明:我之所以说不太好是因为这会导致周期性重启,对许多人来说可能并不理想。而且简单地尝试启动可能会导致问题,因为如果创建了重复实例,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

感谢您在问题中提供的解决方案(以及启发我的答案 :-)),它对我很有用,即使使用多个worker(Rails 3.2.9、Ruby 1.9.3p327)也能正常工作。

我担心当我对lib进行一些更改后,可能会忘记重新启动delayed_job,在意识到这一点之前,需要花费数小时来调试。

为了允许问题中提供的代码每次启动rails时执行,但不是每次worker启动时执行,我将以下内容添加到我的script/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时被调用。我没有花太多时间寻找解决方案,但欢迎提出建议 :-)

请注意,如果您正在使用unicorn,您可能希望在before_fork调用之前将相同的代码添加到config/unicorn.rb中。

--编辑: 在尝试上述解决方案后,我最终采取了以下措施:

我创建了一个名为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}"

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