浩文 联系 关于本站 登录 注册

Active Job

commit信息:Fix typo | 提交者:rhombl4 | 提交时间:2020-10-30 | 版本:db5ff8d

Rails 4.2引进了Active Job。 Active Job是跟job runners交流的标准接口。可以配置Active Job与Sidekiq一起工作。

Active Job 设置

Active Job 的适配器必须设置为:sidekiq,否则它将使用Rails设置的默认值:async。这可以在config/application.rb文件中设置:

class Application < Rails::Application
  # ...
  config.active_job.queue_adapter = :sidekiq
end

我们可以使用生成器来新建一个作业。

rails generate job Example

以上命令将创建 app/jobs/example_job.rb文件

class ExampleJob < ActiveJob::Base
  # Set the Queue as Default
  queue_as :default

  def perform(*args)
    # Perform Job
  end
end

使用

可以将Jobs从任意地方加入到任务队列。我们可以使用以下代码往队列里添加作业:

ExampleJob.perform_later args

然后,Sidekiq将会为我们完成这个作业。当作业由于一些原因失败了,Sidekiq将照常重试它。

定制错误支持

在Rails 6.0.1之前,Active Job 不能充分并且轻松的支持Sidekiq的retry功能。取而代之的是,当遇到特定的异常时,它有一个更简陋的抽象来编码重试

class ExampleJob < ActiveJob::Base
  retry_on ErrorLoadingSite, wait: 5.minutes, queue: :low_priority
  def perform(*args)
    # Perform Job
  end
end

默认的Active Job重试方案(使用retry_on)为5次重试,间隔为3秒。完成这些(大概需要15-30秒),Active Job将把这个作业踢回Sidekiq,然后Sidekiq的指数级回退重试功能将接管该作业。

自Sidekiq 6.0.1起,你可以在大于Rails 6.0.1版的Active Jobs里使用sidekiq_options来配置标准的Sidekiq重试机制。

class ExampleJob < ActiveJob::Base
  sidekiq_options retry: 5

  def perform(*args)
    # Perform Job
  end
end

Active Mailer

Action Mailer 现在拥有一个名为 #deliver_later 的方法,该方法将异步发送email(在后台作业中发送你的email)。只要Active Job被设置为使用Sidekiq,我们就可以使用 #deliver_later 。不像Sidekiq,Active Job将使用全局ID序列化任何activerecord实例。 之后,该实例将被反序列化。

Mailers 被排队在mailers队列。不要忘了开启sidekiq来处理该队列:

bundle exec sidekiq -q default -q mailers

我们可以使用下面代码来发送一个基础信息给作业队列:

UserMailer.welcome_email(@user).deliver_later

当你想绕过作业队列来同步执行该作业时,你可以使用下面代码:

UserMailer.welcome_email(@user).deliver_now

使用Sidekiq,我们可以选择发送带有一定延迟的emails。我们也可以通过Active Job来做到这一点。

Sidekiq中延迟消息的旧语法:

UserMailer.delay_for(1.hour).welcome_email(@user.id)
UserMailer.delay_until(5.days.from_now).welcome_email(@user.id)

通过Active Job来延迟消息的新语法:

UserMailer.welcome_email(@user).deliver_later(wait: 1.hour)
UserMailer.welcome_email(@user).deliver_later(wait_until: 5.days.from_now)

使用全局ID

Rails的全局ID功能允许你给#perform传递ActiveRecord 模型来作为参数,从而:

def perform(user_id)
  user = User.find(user_id)
  user.send_welcome_email!
end

可以被这样代替:

def perform(user)
  user.send_welcome_email!
end

不幸的是,这意味着如果User记录在作业排队之后但在 perform调用之前这段时间内被删除了,异常处理将是不同的。使用规范的Sidekiq,你可以使用以下代码处理该问题:

def perform(user_id)
  user = User.find_by(id: user_id)

  if user
    user.send_welcome_email!
  else
    # handle a deleted user record
  end
end

使用Active Job,作为反序列化User实例的一部分,perform(user)将代替raise来触发一个缺失记录异常。

你可以像下面这样解决此问题:

class MyJob < ActiveJob::Base
  rescue_from ActiveJob::DeserializationError do |exception|
    # handle a deleted user record
  end

  # ...
end

Job ID

Active Job 有自己的Job ID,Sidekiq不使用该ID。你可以使用provider_job_id来得到Sidekiq的JID。

job = SomeJob.perform_later
jid = job.provider_job_id

性能

基准测试显示Active Job把作业推送到Redis要慢2-20倍,并且处理开销约为3倍(使用Rails 5.1.4 搭配 Sidekiq 5.1.1)。由于所有基准测试的性能可能会因版本而异,因此YMMV。

队列前缀

Active Job允许你配置队列的前缀。不要使用“环境特殊”的前缀。每个环境应该使用完全独立的Redis数据库,否则,你的所有环境将共享相同的重试和计划设置,这可能会发生混乱。

商业功能

如果你的Active Job试着使用一些Sidekiq Pro和Sidekiq商业版的功能,它可能被不可预料的打断。例如,在基础情况下,在批处理中创建Active Job是能行的,但是如果你在这些作业中使用Active Job的重试机制的话是会失败的。当有疑问时,不要将Active Job与Sidekiq native APIs结合使用。