如何在Rails启动时创建线程?

时间:2013-01-19 10:28:35

标签: ruby-on-rails multithreading configuration initialization

我正在尝试在Rails启动时创建一个线程,该线程将在应用程序的整个生命周期内运行。奇怪的是,我已经使用了另一个我正在运行的线程。我复制了(工作)代码并将其用作新线程的新代码的样板。但线程不会启动。

  • 代码在config / initializers中(这是正确的位置吗?)。
  • 文件名称以'z _...'开头,以确保它最后运行。
  • Rails 3.2.6

以下是代码的一般结构:

class Blah
  def self.the_thread
    The_Model.transaction do
    # do some database stuff
    # ...
  end

  TheThread = Thread.new do
    while true do
      the_thread
      sleep 5.seconds
    end
  end
end

打开一个rails控制台并检查Blah :: TheThread会显示一个似乎没有运行的死线程。在类的声明中似乎没有任何错误,也没有在方法中,因为我可以在打开rails控制台时运行该方法并且它工作得很好。此外,如果我手动输入rails控制台上面产生线程的确切代码(TheThread = Thread new do ...),它就可以正常工作(每隔5秒唤醒一次,做它的事情,再次睡觉)。

同样,奇怪的是,在这个完全相同的应用程序中,这个用于在Rails中生成一个简单线程的精确样板在我之前工作过。

如果有人对我认为是一个奇怪的问题有一些可能的见解,我会全力以赴。

感谢。

编辑:新信息 - 我只是注释掉了交易调用(和匹配结束),它运行正常。我不知道交易可能会干扰什么。我删除了我的其他自定义初始化程序脚本,目录中唯一的其他程序是默认的,或属于设计的。我仔细阅读了它们,并没有看到任何交易。

更多新信息。我在线程代码的末尾添加了'TheThread.abort_on_exception = true'。当我启动rails时,我现在得到一个异常'ArgumentError:prepare on a closed database:rollback transaction(ActiveRecord :: StatementInvalid)'。错误发生在sqlite3 gem中。我有sqlite3 gem版本1.3.6和sqlite-ruby gem版本1.3.3。我已经搜索了错误并发现有几个帖子抱怨同样的错误,但没有看到任何解决方案。

我不再认为这是一个线程问题,而是一个数据库问题,但更改这篇文章的标题为时已晚。

这就是:我在线程代码的顶部放置了一个'sleep 15.seconds'(就在它进入while循环之前)并且清除了问题。但我仍然想知道是什么导致了这个问题,以及什么是“非黑客”解决方案。出于某种原因,此代码运行时数据库尚未就绪。我的初始化代码是否只是在错误的位置?

1 个答案:

答案 0 :(得分:1)

我真的建议另外一种方法来实现它。除非你去生产时真的需要一个额外的线程每个实例(想象设置3-4个瘦/杂种/等服务器 - 每个都有额外的线程),有更好的方法去做它

选项1 - 最简单 - 只是创建一个执行任务的控制器操作,然后使用wget和cron定期触发该操作。

20 * * * *  /usr/bin/wget --quiet -O - 'http://www.mydomain.com/my_controller/my_action'

这里的问题是你必须处理身份验证/访问控制 - 除非你真的不在乎某个随机用户是否可以触发你的工作(坦率地说,有时非常简单的工作就是这种情况)。

选项2 - 在后台使用Clockwork / Sidekiq或BackgroundRb或其他一些运行任务的方式。我在一个生产应用程序中完成了这个,它的工作非常好。我选择了Clockwork和Sidekiq,我的预定任务看起来像这样:

应用程序/工人/ daily_worker.rb:

class DailyWorker
  include Sidekiq::Worker

  def perform
    # do stuff here
  end
end

clockwork.rb:

require 'clockwork'
require 'sidekiq'

Dir["app/workers/*"].each {|f| load f }

module Clockwork
  every 1.day, 'daily.jobs', :at => "00:30" do
    DailyWorker.perform_async
  end
end

这对我来说似乎是最好的解决方案 - 而且我只花了大约15分钟才能完成并运行。