是否可以使用Ruby Threading终止已经运行的延迟作业?

时间:2011-04-06 11:52:29

标签: ruby-on-rails-3 delayed-job

假设我在后台运行了delayed_job。任务可以安排或立即运行(有些是长期任务,有些则不是)

如果任务太长,用户应该可以取消它。延迟工作有可能吗?我检查了文档,似乎无法找到终止方法或其他东西。它们只提供一个捕获来取消延迟的作业本身(因此取消所有任务......我只需要取消某个正在运行的任务)

更新 我的老板(谁是一个伟大的程序员顺便说一句)建议使用Ruby Threading来实现我们的这个功能。这可能吗?就像为每个任务创建新线程并在线程运行时终止该线程一样?

类似的东西:

t1 = Thread.new(task.run)
self.delay.t1.join (?) -- still reading on threads so correct me if im wrong

然后停止它我将只使用t1.stop(?)再次不知道

这可能吗?谢谢!

4 个答案:

答案 0 :(得分:3)

似乎我的老板到了现场,所以这就是我们所做的(请告诉我们是否有可能这是不好的做法,所以我可以提出来):

  1. 首先,我们有一个具有def执行功能的Job模型! (它运行它应该做的)。
  2. 接下来,我们在后台有 delayed_job 工作人员,正在侦听新工作。现在,当您创建一个作业时,您可以安排它立即运行或每隔一天运行(我们使用rufus)
  3. 创建作业时,会检查是否应立即运行。如果是,则将其自身添加到延迟的作业队列中。 execute函数创建一个Thread,因此每个作业都有自己的线程。
  4. ui中的用户可以查看作业是否正在运行(如果有一个started_at且没有finished_at)。如果它正在运行,则有一个取消它的按钮。取消它只是将作业的delayed_at设置为Time.now。
  5. 当作业正在运行时,它还会检查自己是否有cancel_at或Time.now是否> finished_at。如果是这样,请杀死该线程。
  6. 瞧!我们为一项工作测试了它,它似乎工作。现在唯一的问题是缩放......

    如果你发现任何问题,请在评论中这样做或提出更多建议:)我希望这对一些人也有帮助!

答案 1 :(得分:2)

Delayed::Job< ActiveRecord::Base模型,因此您可以像平常一样查询Delayed::Job.all(:conditions => {:last_error => nil})

Delayed :: Job对象有一个有效负载字段,其中包含您尝试运行的方法或作业的序列化版本。这个对象可以通过'#payload_object'方法访问,该方法加载有问题的对象。

您可以将这两个功能组合在一起,以创建可查询的工作工作人员,例如,如果您有User模型,并且用户有一个回形针:avatar,那么您可以创建一个方法像这样删除未经处理的作业:

class User < ActiveRecord::Base
   has_attached_file :avatar, PaperclipOptions.new(:avatar)
   before_create :'process_avatar_later'

   def process_avatar_later
      filename = Rails.root.join('tmp/avatars_for_processing/',self.id)
      open(filename, 'w') do |file| file <<self.avatar.to_file end
      Delayed::Job.enqueue(WorkAvatar.new(self.id, filename))
      self.avatar = nil
   end

   def cancel_future_avatar_processing
      WorkAvatar.future_jobs_for_user(self.id).each(&:destroy)
      #ummm... tell them to reupload their avatar, I guess?
   end

   class WorkAvatar < Struct.new(:user_id, :path)
     def user
        @user ||= User.find(self.user_id)
     end
     def self.all_jobs
       Delayed::Job.scoped(:conditions => 'payload like "%WorkAvatar%"')
     end
     def self.future_jobs_for_user(user_id)
       all_jobs.scoped(:conditions => {:locked_at => nil}).select do |job|
          job.payload_object.user_id == user_id
       end
     end  
     def perform
        @user.avatar = File.open(path, 'rb')
        @user.save()
     end
   end          
end

有人可能会制作一个插件,使这样的可查询对象。也许在GitHub上搜索会很有成效。

另请注意,如果要取消具有locked_at和{{1}的作业,则必须使用任何流程监视工具来取消正在执行的正在运行的作业工作进程。 } set。

答案 2 :(得分:1)

您可以将任务包装到Timeout语句中。

require 'timeout'

class TaskWithTimeout < Struct.new(:parameter)
  def perform
    Timeout.timeout(10) do
      # ...
    end
  rescue Timeout::Error => e
    # the task took longer than 10 seconds
  end
end

答案 3 :(得分:0)

不,没有办法做到这一点。如果你担心一个失控的工作,你肯定应该像Simone建议的那样把它包裹起来。然而,听起来你正在寻找更多的东西,但我不清楚你的最终目标。

用户永远不会有“取消”按钮的方法,因为这将涉及找到一种方法来直接与运行该作业的工作人员运行进程通信。可以向worker添加一个信号处理程序,这样你就可以执行kill -USR1 pid之类的操作,让它中止当前正在工作的作业并继续。这会实现你的目标吗?