我们必须使用delayed_job(或其他一些后台作业处理器)在后台运行作业,但我们不允许更改服务器上的启动脚本/启动级别。这意味着如果提供程序重新启动服务器,则不保证守护程序仍然可用(因为守护程序将由每个部署仅运行一次的capistrano配方启动)。
目前,我能想到确保delayed_job守护程序始终运行的最佳方法是向我们的Rails应用程序添加一个初始化程序,以检查守护程序是否正在运行。如果它没有运行,则初始化程序启动守护程序,否则,它只是保留它。
因此,问题是我们如何检测到Delayed_Job守护程序是从脚本内部运行的? (我们应该能够相当容易地启动一个守护进程,我不知道如何检测一个守护进程是否已经激活)。
有人有什么想法吗?
此致 伯尼
基于下面的答案,这就是我想出的。只需将它放入配置/初始化程序中即可完成设置:
#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
答案 0 :(得分:9)
更多清理想法:不需要“开始”。你应该拯救“没有这样的过程”,以便在出现其他问题时不要启动新进程。拯救“没有这样的文件或目录”以简化条件。
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”参数,该参数与守护进程一起生成监视进程。
答案 1 :(得分:5)
检查是否存在守护进程PID文件(File.exist? ...
)。如果它在那里,那么假设它正在运行,否则启动它。
答案 2 :(得分:0)
感谢您提供问题中提供的解决方案(以及启发它的答案:-)),它适用于我,即使有多个工作人员(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}"
答案 3 :(得分: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