Ruby on Rails:如何在后台运行任务?

32

当创建新资源并且需要进行一些冗长的处理以使资源准备就绪时,我该如何将该处理发送到后台,以便不会影响当前请求或其他流量到我的Web应用程序?

在我的模型中:

class User < ActiveRecord::Base
 after_save :background_check

 protected
 def background_check
  # check through a list of 10000000000001 mil different
  # databases that takes approx one hour :)
  if( check_for_record_in_www( self.username ) )
    # code that is run after the 1 hour process is finished.
    user.update_attribute( :has_record )
  end
 end
end
7个回答

42
您绝对应该查看以下Railscasts: 它们详细解释了如何以各种可能的方式在Rails中运行后台进程(有或没有队列...)。

2
现在,最常用/最好支持的选项是 sidekiqresquedelayed_job。在我看来,Sidekiq 是高吞吐量队列的最佳选择,而 delayed_job 是非常低吞吐量的最佳选择。 - John Douthat
另一个选择是IronWorker,如果您想要作为服务的后台处理,并且永远不必担心服务器或排队的概念。IronMQ/IronWorker还提供自动重试、任务日志、Webhooks等功能。它与Rails集成得很好。以下是一些视频:https://www.youtube.com/user/ironiodevelopers - Chad
有用的链接,这些年仍然适用,但它们并没有总结它们或直接回答问题。 - Peter DeWeese

8

我刚刚试验了“delayed_job”宝石,因为它可以与Heroku托管平台一起使用,并且设置非常简单!

将宝石添加到Gemfile中,运行bundle installrails g delayed_jobrake db:migrate命令进行安装。然后使用以下命令启动队列处理程序:

RAILS_ENV=production script/delayed_job start

当你有一个耗时的方法调用时,比如:

company.send_mail_to_all_users

你需要将它改为:
company.delay.send_mail_to_all_users

请查看Github上的完整文档:https://github.com/collectiveidea/delayed_job


7

启动一个单独的进程,最简单的方法可能是使用 system 命令,将 'nohup' 添加到你传递给它的命令开头,然后在命令末尾添加 '&' 符号。(确保命令只有一个字符串参数,而不是参数列表。)

这样做有几个原因,而不是尝试使用线程:

  1. Ruby 的线程在进行 I/O 操作时可能会有些棘手;你必须小心一些操作不会导致整个进程阻塞。

  2. 如果你用不同的名称运行程序,在 'ps' 中很容易识别,这样你就不会错误地认为它是 FastCGI 后端出了问题,而将其杀死。

实际上,你启动的进程应该是“守护进程”,可以查看 Daemonize 类以获取帮助。


2

最好使用现有的后台作业服务器,而不是编写自己的。 这些通常允许您提交作业并为其提供唯一的密钥; 然后,您可以使用该密钥定期查询作业服务器以获取作业的状态,而不会阻塞您的Web应用程序。 这里有各种选项的精选汇总。


1
我认为spawn是一个很好的方式来分叉你的进程,在后台进行一些处理,并向用户显示一些确认信息,表明这个处理已经开始。

1

我喜欢使用backgroundrb,它非常好,可以在长时间的处理过程中与其进行通信。这样你就可以在你的rails应用程序中获得状态更新。


0

这个怎么样:

def background_check
   exec("script/runner check_for_record_in_www.rb #{self.username}") if fork == nil
end

程序"check_for_record_in_www.rb"将在另一个进程中运行,并且可以访问ActiveRecord,以便访问数据库。


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