4

有了delayed_job,我可以做这样的简单操作:

@foo.delay.increment!(:myfield)

是否可以对 Rails 的新 ActiveJob 做同样的事情?(无需创建一大堆执行这些小操作的作业类)

4

3 回答 3

1

据我所知,目前不支持此功能。您可以使用接受模型或实例、要执行的方法和参数列表的自定义代理作业轻松模拟此功能。

但是,出于代码测试和可维护性的考虑,这种快捷方式并不是一个好方法。为您想要排队的所有内容制定特定的工作会更有效(即使您需要编写更多代码)。它迫使您更多地考虑应用程序的设计。

于 2015-06-21T09:35:09.890 回答
1

ActiveJob只是各种后台作业处理器之上的抽象,因此许多功能取决于您实际使用的提供者。但我会尽量不依赖任何后端。

通常,作业提供者持久性机制和运行器组成。卸载作业时,您以某种方式将其写入持久性机制,然后其中一个运行者检索它并运行它。所以问题是:您能否以一种与您需要的任何操作兼容的格式来表达您的工作数据?

那会很棘手。

让我们定义什么是工作定义。例如,它可以是单个方法调用。假设这种语法:

Model.find(42).delay.foo(1, 2)

我们可以使用以下格式:

{
  class: 'Model',
  id: '42', # whatever
  method: 'foo',
  args: [
    1, 2
  ]
}

现在我们如何从给定的调用构建这样的哈希并将其排入作业队列?

首先,看起来,我们需要定义一个类method_missing来捕获被调用的方法名:

class JobMacro
  attr_accessor :data
  def initialize(record = nil)
    self.data = {}
    if record.present?
      self.data[:class] = record.class.to_s
      self.data[:id]    = record.id
    end
  end
  def method_missing(action, *args)
    self.data[:method] = action.to_s
    self.data[:args] = args
    GenericJob.perform_later(data)
  end
end

作业本身必须像这样重建该表达式:

data[:class].constantize.find(data[:id]).public_send(data[:method], *data[:args])

当然,您必须delay在模型上定义宏。最好将其分解为一个模块,因为定义非常通用:

def delay
  JobMacro.new(self)
end

它确实有一些限制:

  • 仅支持在持久的 ActiveRecord 模型上运行作业。一项工作需要一种方法来重建被调用者以调用该方法,我选择了最可能的方法。如果需要,您也可以使用编组,但我认为这不可靠:在作业开始执行时,未编组的对象可能无效。关于“GlobalID”也是如此。
  • 它使用 Ruby 的反射。这是解决许多问题的诱人解决方案,但速度不快,并且在安全性方面有点风险。所以谨慎使用这种方法。
  • 只有一个方法调用。没有 procs(你可以用ruby2rubygem 做到这一点)。依靠作业提供者正确序列化参数,如果失败,请使用您自己的代码帮助它。例如,que在内部使用 JSON,所以无论在 JSON 中如何工作,都可以在que. 例如,符号不会。

一开始事情会以惊人的方式破裂。
因此,请确保在开始之前设置您的调试工具。


这方面的一个例子是Sidekiq 的 ActiveRecord 的向后 (Delayed::Job) 兼容性扩展

于 2015-06-21T09:39:41.333 回答
0

我写了一个 gem 可以帮助你解决这个问题 https://github.com/cristianbica/activejob-perform_later。但是请注意,我相信在您的代码周围有可能在工作人员中执行的方法是灾难的完美秘诀,没有小心处理:)

于 2015-08-20T20:59:32.393 回答