1. 什么是 Active Job?
Active Job 是 Rails 中一个用于声明后台作业并在队列后端上执行它们的框架。它为发送电子邮件、处理数据或处理常规维护活动(如清理和计费)等任务提供了标准化的接口。通过将这些任务从主应用程序线程卸载到默认的 Solid Queue 等队列后端,Active Job 确保耗时操作不会阻塞请求-响应循环。这可以提高应用程序的性能和响应能力,使其能够并行处理任务。
2. 创建并入队作业
本节将提供创建和入队作业的分步指南。
2.1. 创建作业
Active Job 提供了一个 Rails 生成器来创建作业。以下将在 app/jobs 中创建一个作业(并在 test/jobs 下附带一个测试用例)
$ bin/rails generate job guests_cleanup
invoke test_unit
create test/jobs/guests_cleanup_job_test.rb
create app/jobs/guests_cleanup_job.rb
您还可以创建一个将在特定队列上运行的作业
$ bin/rails generate job guests_cleanup --queue urgent
如果您不想使用生成器,您可以在 app/jobs 中创建自己的文件,只需确保它继承自 ApplicationJob。
这是一个作业的示例
class GuestsCleanupJob < ApplicationJob
queue_as :default
def perform(*guests)
# Do something later
end
end
请注意,您可以根据需要定义任意数量参数的 perform 方法。
如果您已经有一个抽象类,并且其名称与 ApplicationJob 不同,您可以传递 --parent 选项来指示您想要一个不同的抽象类
$ bin/rails generate job process_payment --parent=payment_job
class ProcessPaymentJob < PaymentJob
queue_as :default
def perform(*args)
# Do something later
end
end
2.2. 入队作业
使用 perform_later(可选地,set)入队作业。如下所示
# Enqueue a job to be performed as soon as the queuing system is
# free.
GuestsCleanupJob.perform_later guest
# Enqueue a job to be performed tomorrow at noon.
GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)
# Enqueue a job to be performed 1 week from now.
GuestsCleanupJob.set(wait: 1.week).perform_later(guest)
# `perform_now` and `perform_later` will call `perform` under the hood so
# you can pass as many arguments as defined in the latter.
GuestsCleanupJob.perform_later(guest1, guest2, filter: "some_filter")
就是这样!
2.3. 批量入队作业
您可以使用 perform_all_later 一次性入队多个作业。有关更多详细信息,请参阅 批量入队。
3. 默认后端: Solid Queue
Solid Queue,从 Rails 8.0 版本开始默认启用,是一个基于数据库的 Active Job 队列系统,允许您排队大量数据而无需额外的依赖项,例如 Redis。
除了常规的作业入队和处理之外,Solid Queue 还支持延迟作业、并发控制、每个作业的数字优先级、按队列顺序的优先级等等。
3.1. 设置
3.1.1. 开发环境
在开发环境中,Rails 提供了一个异步的进程内队列系统,它将作业保存在 RAM 中。如果进程崩溃或机器重置,则使用默认的异步后端时所有未完成的作业都将丢失。这对于小型应用程序或开发环境中非关键作业来说可能没问题。
但是,如果您使用 Solid Queue,您可以像在生产环境中一样配置它
# config/environments/development.rb
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = { database: { writing: :queue } }
这会将 :solid_queue 适配器设置为开发环境中 Active Job 的默认适配器,并连接到 queue 数据库进行写入。
此后,您需要将 queue 添加到开发数据库配置中
# config/database.yml
development:
primary:
<<: *default
database: storage/development.sqlite3
queue:
<<: *default
database: storage/development_queue.sqlite3
migrations_paths: db/queue_migrate
数据库配置中的键 queue 需要与 config.solid_queue.connects_to 配置中使用的键匹配。
然后您可以运行 db:prepare 以确保 development 中的 queue 数据库具有所有必需的表
$ bin/rails db:prepare
您可以在 db/queue_schema.rb 中找到 queue 数据库的默认生成架构。它们将包含 solid_queue_ready_executions、solid_queue_scheduled_executions 等表。
最后,要启动队列并开始处理作业,您可以运行
bin/jobs start
3.1.2. 生产环境
Solid Queue 已为生产环境配置好。如果您打开 config/environments/production.rb,您将看到以下内容
# config/environments/production.rb
# Replace the default in-process and non-durable queuing backend for Active Job.
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = { database: { writing: :queue } }
此外,queue 数据库的数据库连接在 config/database.yml 中配置
# config/database.yml
# Store production database in the storage/ directory, which by default
# is mounted as a persistent Docker volume in config/deploy.yml.
production:
primary:
<<: *default
database: storage/production.sqlite3
queue:
<<: *default
database: storage/production_queue.sqlite3
migrations_paths: db/queue_migrate
确保运行 db:prepare 以便您的数据库可以使用
$ bin/rails db:prepare
3.2. 配置
Solid Queue 的配置选项在 config/queue.yml 中定义。以下是默认配置的示例
default: &default
dispatchers:
- polling_interval: 1
batch_size: 500
workers:
- queues: "*"
threads: 3
processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
polling_interval: 0.1
为了理解 Solid Queue 的配置选项,您必须理解不同类型的角色
- 调度器 (Dispatchers): 它们选择计划在未来运行的作业。当这些作业该运行时,调度器将它们从
solid_queue_scheduled_executions表移动到solid_queue_ready_executions表,以便工作者可以拾取它们。它们还管理与并发相关的维护。 - 工作者 (Workers): 它们拾取准备运行的作业。这些作业从
solid_queue_ready_executions表中获取。 - 调度器 (Scheduler): 它负责周期性任务,在任务到期时将作业添加到队列中。
- 监督器 (Supervisor): 它监督整个系统,管理工作者和调度器。它根据需要启动和停止它们,监控它们的健康状况,并确保一切顺利运行。
config/queue.yml 中的所有内容都是可选的。如果没有提供配置,Solid Queue 将使用一个调度器和一个工作者以默认设置运行。以下是您可以在 config/queue.yml 中设置的一些配置选项
| 选项 | 描述 | 默认值 |
|---|---|---|
| polling_interval (轮询间隔) | 工作者/调度器在检查更多作业之前等待的时间(秒)。 | 1 秒(调度器),0.1 秒(工作者) |
| batch_size (批处理大小) | 一批次调度的作业数量。 | 500 |
| concurrency_maintenance_interval (并发维护间隔) | 调度器在检查可以解除阻塞的被阻塞作业之前等待的时间(秒)。 | 600 秒 |
| queues (队列) | 工作者从中获取作业的队列列表。支持 * 表示所有队列或队列名称前缀。 |
* |
| threads (线程) | 每个工作者的线程池的最大大小。确定工作者一次获取多少作业。 | 3 |
| processes (进程) | 监督器分叉的工作者进程数量。每个进程可以专用于一个 CPU 核心。 | 1 |
| concurrency_maintenance (并发维护) | 调度器是否执行并发维护工作。 | true |
您可以在 Solid Queue 文档中阅读有关这些配置选项的更多信息。还有一些 其他配置选项 可以在 config/<environment>.rb 中设置,以进一步配置 Rails 应用程序中的 Solid Queue。
3.3. 队列顺序
根据 配置部分 中的配置选项,queues 配置选项将列出工作者将从中获取作业的队列。在队列列表中,顺序很重要。工作者将从列表中的第一个队列中获取作业——一旦第一个队列中没有更多作业,它才会转到第二个,依此类推。
# config/queue.yml
production:
workers:
- queues:[active_storage*, mailers]
threads: 3
polling_interval: 5
在上面的示例中,工作者将从以 "active_storage" 开头的队列中获取作业,例如 active_storage_analyse 队列和 active_storage_transform 队列。只有当 active_storage-前缀队列中没有作业时,工作者才会转到 mailers 队列。
通配符 *(例如 "active_storage" 的末尾)仅允许单独使用或在队列名称的末尾以匹配所有具有相同前缀的队列。您不能指定诸如 *_some_queue 之类的队列名称。
在 SQLite 和 PostgreSQL 中使用通配符队列名称(例如 queues: active_storage*)可能会降低轮询性能,因为需要进行 DISTINCT 查询来识别所有匹配的队列,这在这些 RDBMS 的大表上可能会很慢。为了获得更好的性能,最好指定确切的队列名称而不是使用通配符。在 Solid Queue 文档中的队列规范和性能 中阅读更多相关信息
Active Job 在入队作业时支持正整数优先级(参见 优先级部分)。在单个队列中,作业根据其优先级进行选择(整数越小优先级越高)。但是,当您有多个队列时,队列本身的顺序优先。
例如,如果您有两个队列,production 和 background,production 队列中的作业将始终首先处理,即使 background 队列中的某些作业具有更高的优先级。
3.4. 线程、进程和信号
在 Solid Queue 中,并行性通过线程(可通过 threads 参数 配置)、进程(可通过 processes 参数)或横向扩展实现。监督器管理进程并响应以下信号
- TERM, INT: 启动优雅终止,发送 TERM 信号并等待最多
SolidQueue.shutdown_timeout。如果未完成,QUIT 信号强制进程退出。 - QUIT: 强制立即终止进程。
如果工作者意外终止(例如,使用 KILL 信号),正在进行的作业将被标记为失败,并引发 SolidQueue::Processes::ProcessExitError 或 SolidQueue::Processes::ProcessPrunedError 等错误。心跳设置有助于管理和检测过期进程。在 Solid Queue 文档中阅读有关线程、进程和信号的更多信息。
3.5. 入队时发生错误
当作业入队时发生 Active Record 错误时,Solid Queue 会引发 SolidQueue::Job::EnqueueError。这与 Active Job 引发的 ActiveJob::EnqueueError 不同,后者处理错误并使 perform_later 返回 false。这使得由 Rails 或第三方 gem(如 Turbo::Streams::BroadcastJob)入队的作业的错误处理变得更加棘手。
对于周期性任务,入队时遇到的任何错误都将被记录,但不会被引发。在 Solid Queue 文档中阅读有关入队时发生错误的更多信息。
3.6. 并发控制
Solid Queue 扩展了 Active Job 的并发控制,允许您限制特定类型或具有特定参数的作业同时运行的数量。如果作业超出限制,它将被阻塞,直到另一个作业完成或持续时间到期。例如
class MyJob < ApplicationJob
limits_concurrency to: 2, key: ->(contact) { contact.account }, duration: 5.minutes
def perform(contact)
# perform job logic
end
end
在此示例中,相同账户的 MyJob 实例将只有两个同时运行。之后,其他作业将被阻塞,直到其中一个完成。
group 参数可用于控制不同作业类型之间的并发。例如,两个使用相同组的不同作业类将一起限制其并发
class Box::MovePostingsByContactToDesignatedBoxJob < ApplicationJob
limits_concurrency key: ->(contact) { contact }, duration: 15.minutes, group: "ContactActions"
end
class Bundle::RebundlePostingsJob < ApplicationJob
limits_concurrency key: ->(bundle) { bundle.contact }, duration: 15.minutes, group: "ContactActions"
end
这确保了给定联系人只有一个作业可以运行,无论作业类是什么。
在 Solid Queue 文档中阅读有关并发控制的更多信息。
3.7. 作业错误报告
如果您的错误跟踪服务没有自动报告作业错误,您可以手动挂接到 Active Job 以报告它们。例如,您可以在 ApplicationJob 中添加一个 rescue_from 块
class ApplicationJob < ActiveJob::Base
rescue_from(Exception) do |exception|
Rails.error.report(exception)
raise exception
end
end
如果您使用 ActionMailer,您需要单独处理 MailDeliveryJob 的错误
class ApplicationMailer < ActionMailer::Base
ActionMailer::MailDeliveryJob.rescue_from(Exception) do |exception|
Rails.error.report(exception)
raise exception
end
end
3.8. 作业上的事务完整性
⚠️ 将您的作业放在与您的应用程序数据相同的符合 ACID 标准的数据库中,启用了一个强大但锋利的工具:利用事务完整性来确保您的应用程序中的某个操作只有在您的作业也提交后才提交,反之亦然,并确保您的作业只有在您入队的事务提交后才入队。这可能非常强大和有用,但如果您将部分逻辑建立在这种行为上,并且将来您迁移到另一个 Active Job 后端,或者您只是将 Solid Queue 移动到自己的数据库中,然后行为突然改变,这也会适得其反。
由于这可能非常棘手,并且许多人不需要担心它,因此默认情况下 Solid Queue 配置为与主应用程序不同的数据库。
但是,如果您在与应用程序相同的数据库中使用 Solid Queue,您可以通过 Active Job 的 enqueue_after_transaction_commit 选项确保您不会意外地依赖事务完整性,该选项可以为单个作业或通过 ApplicationJob 为所有作业启用
class ApplicationJob < ActiveJob::Base
self.enqueue_after_transaction_commit = true
end
您还可以将 Solid Queue 配置为使用与您的应用程序相同的数据库,同时通过为 Solid Queue 作业设置单独的数据库连接来避免依赖事务完整性。在 Solid Queue 文档中阅读有关事务完整性的更多信息
3.9. 周期性任务
Solid Queue 支持周期性任务,类似于 cron 作业。这些任务在配置文件(默认情况下是 config/recurring.yml)中定义,可以安排在特定时间运行。这是一个任务配置的示例
production:
a_periodic_job:
class: MyJob
args: [42, { status: "custom_status" }]
schedule: every second
a_cleanup_task:
command: "DeletedStuff.clear_all"
schedule: every day at 9am
每个任务都指定一个 class 或 command 和一个 schedule(使用 Fugit 解析)。您还可以向作业传递参数,例如在 MyJob 的示例中,其中传递了 args。这可以作为单个参数、哈希或参数数组传递,参数数组还可以包含 kwargs 作为数组的最后一个元素。这允许作业在指定时间周期性地运行。
在 Solid Queue 文档中阅读有关周期性任务的更多信息。
3.10. 作业跟踪和管理
像 mission_control-jobs 这样的工具可以帮助集中监控和管理失败的作业。它提供了对作业状态、失败原因和重试行为的深入了解,使您能够更有效地跟踪和解决问题。
例如,如果一个作业由于超时而未能处理大文件,mission_control-jobs 允许您检查故障,审查作业的参数和执行历史,并决定是重试、重新入队还是丢弃它。
4. 队列
使用 Active Job,您可以使用 queue_as 将作业安排在特定队列上运行
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
# ...
end
您可以使用 application.rb 中的 config.active_job.queue_name_prefix 为所有作业添加队列名称前缀
# config/application.rb
module YourApp
class Application < Rails::Application
config.active_job.queue_name_prefix = Rails.env
end
end
# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
# ...
end
# Now your job will run on queue production_low_priority on your
# production environment and on staging_low_priority
# on your staging environment
您还可以按作业配置前缀。
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
self.queue_name_prefix = nil
# ...
end
# Now your job's queue won't be prefixed, overriding what
# was configured in `config.active_job.queue_name_prefix`.
默认的队列名称前缀分隔符是 '_'。可以通过在 application.rb 中设置 config.active_job.queue_name_delimiter 来更改
# config/application.rb
module YourApp
class Application < Rails::Application
config.active_job.queue_name_prefix = Rails.env
config.active_job.queue_name_delimiter = "."
end
end
# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
# ...
end
# Now your job will run on queue production.low_priority on your
# production environment and on staging.low_priority
# on your staging environment
要从作业级别控制队列,您可以向 queue_as 传递一个块。该块将在作业上下文中执行(因此它可以访问 self.arguments),并且它必须返回队列名称
class ProcessVideoJob < ApplicationJob
queue_as do
video = self.arguments.first
if video.owner.premium?
:premium_videojobs
else
:videojobs
end
end
def perform(video)
# Do process video
end
end
ProcessVideoJob.perform_later(Video.last)
如果您想更精确地控制作业将在哪个队列中运行,您可以向 set 传递一个 :queue 选项
MyJob.set(queue: :another_queue).perform_later(record)
如果您选择使用 备用队列后端,您可能需要指定要监听的队列。
5. 优先级
您可以使用 queue_with_priority 安排作业以特定优先级运行
class GuestsCleanupJob < ApplicationJob
queue_with_priority 10
# ...
end
Solid Queue,默认的队列后端,根据队列的顺序来确定作业的优先级。您可以在 队列顺序部分 中阅读更多相关信息。如果您正在使用 Solid Queue,并且同时使用了队列顺序和优先级选项,则队列顺序将优先,并且优先级选项将仅适用于每个队列内。
其他队列后端可能允许作业在同一队列内或跨多个队列相互优先级。有关更多信息,请参阅您的后端文档。
与 queue_as 类似,您还可以向 queue_with_priority 传递一个块,以便在作业上下文中进行评估
class ProcessVideoJob < ApplicationJob
queue_with_priority do
video = self.arguments.first
if video.owner.premium?
0
else
10
end
end
def perform(video)
# Process video
end
end
ProcessVideoJob.perform_later(Video.last)
您还可以向 set 传递一个 :priority 选项
MyJob.set(priority: 50).perform_later(record)
较低的优先级数字是先执行还是后执行,取决于适配器的实现。有关更多信息,请参阅您的后端文档。鼓励适配器作者将较低的数字视为更重要。
6. 回调
Active Job 提供了在作业生命周期中触发逻辑的钩子。像 Rails 中的其他回调一样,您可以将回调实现为普通方法,并使用宏样式类方法将它们注册为回调
class GuestsCleanupJob < ApplicationJob
queue_as :default
around_perform :around_cleanup
def perform
# Do something later
end
private
def around_cleanup
# Do something before perform
yield
# Do something after perform
end
end
宏样式类方法也可以接收一个块。如果您的块中的代码很短,只有一行,请考虑使用这种样式。例如,您可以为每个入队的作业发送指标
class ApplicationJob < ActiveJob::Base
before_enqueue { |job| $statsd.increment "#{job.class.name.underscore}.enqueue" }
end
6.1. 可用回调
before_enqueue (入队前)around_enqueue (入队前后)after_enqueue (入队后)before_perform (执行前)around_perform (执行前后)after_perform (执行后)after_discard (丢弃后)
请注意,当使用 perform_all_later 批量入队作业时,诸如 around_enqueue 之类的回调将不会在单个作业上触发。请参阅 批量入队回调。
7. 批量入队
您可以使用 perform_all_later 一次性入队多个作业。批量入队减少了对队列数据存储(如 Redis 或数据库)的往返次数,使其比单独入队相同作业的性能更高。
perform_all_later 是 Active Job 上的顶级 API。它接受实例化的作业作为参数(请注意,这与 perform_later 不同)。perform_all_later 在底层调用 perform。传递给 new 的参数最终将在调用 perform 时传递给它。
这是一个使用 GuestsCleanupJob 实例调用 perform_all_later 的示例
# Create jobs to pass to `perform_all_later`.
# The arguments to `new` are passed on to `perform`
cleanup_jobs = Guest.all.map { |guest| GuestsCleanupJob.new(guest) }
# Will enqueue a separate job for each instance of `GuestsCleanupJob`
ActiveJob.perform_all_later(cleanup_jobs)
# Can also use `set` method to configure options before bulk enqueuing jobs.
cleanup_jobs = Guest.all.map { |guest| GuestsCleanupJob.new(guest).set(wait: 1.day) }
ActiveJob.perform_all_later(cleanup_jobs)
perform_all_later 会记录成功入队的作业数量,例如,如果上面的 Guest.all.map 导致 3 个 cleanup_jobs,它将记录 Enqueued 3 jobs to Async (3 GuestsCleanupJob)(假设所有作业都已入队)。
perform_all_later 的返回值为 nil。请注意,这与 perform_later 不同,后者返回已排队的作业类的实例。
7.1. 入队多个 Active Job 类
使用 perform_all_later,还可以在同一个调用中入队不同的 Active Job 类实例。例如
class ExportDataJob < ApplicationJob
def perform(*args)
# Export data
end
end
class NotifyGuestsJob < ApplicationJob
def perform(*guests)
# Email guests
end
end
# Instantiate job instances
cleanup_job = GuestsCleanupJob.new(guest)
export_job = ExportDataJob.new(data)
notify_job = NotifyGuestsJob.new(guest)
# Enqueues job instances from multiple classes at once
ActiveJob.perform_all_later(cleanup_job, export_job, notify_job)
7.2. 批量入队回调
当使用 perform_all_later 批量入队作业时,诸如 around_enqueue 之类的回调将不会在单个作业上触发。此行为与其他 Active Record 批量方法一致。由于回调在单个作业上运行,它们无法利用此方法的批量特性。
但是,perform_all_later 方法确实会触发一个 enqueue_all.active_job 事件,您可以使用 ActiveSupport::Notifications 订阅该事件。
方法 successfully_enqueued? 可用于查询给定作业是否已成功入队。
7.3. 队列后端支持
对于 perform_all_later,批量入队需要由队列后端支持。Solid Queue,默认的队列后端,支持使用 enqueue_all 进行批量入队。
其他后端,如 Sidekiq,有一个 push_bulk 方法,可以将大量作业推送到 Redis 并防止往返网络延迟。GoodJob 也支持使用 GoodJob::Bulk.enqueue 方法进行批量入队。
如果队列后端*不*支持批量入队,perform_all_later 将逐个入队作业。
8. Action Mailer
在现代 Web 应用程序中,最常见的作业之一是在请求-响应周期之外发送电子邮件,这样用户就不必等待。Active Job 已与 Action Mailer 集成,因此您可以轻松地异步发送电子邮件
# If you want to send the email now use #deliver_now
UserMailer.welcome(@user).deliver_now
# If you want to send the email through Active Job use #deliver_later
UserMailer.welcome(@user).deliver_later
从 Rake 任务中使用异步队列(例如,使用 .deliver_later 发送电子邮件)通常不起作用,因为 Rake 可能会结束,导致进程内线程池被删除,然后才处理任何/所有 .deliver_later 电子邮件。为避免此问题,请使用 .deliver_now 或在开发环境中运行持久性队列。
9. 国际化
每个作业都使用创建作业时设置的 I18n.locale。这在您异步发送电子邮件时很有用
I18n.locale = :eo
UserMailer.welcome(@user).deliver_later # Email will be localized to Esperanto.
10. 支持的参数类型
ActiveJob 默认支持以下类型的参数
- 基本类型 (
NilClass,String,Integer,Float,BigDecimal,TrueClass,FalseClass) SymbolDateTimeDateTimeActiveSupport::TimeWithZoneActiveSupport::DurationHash(键应为String或Symbol类型)ActiveSupport::HashWithIndifferentAccessArrayRangeModuleClass
10.1. GlobalID
Active Job 支持用于参数的 GlobalID。这使得可以将实时 Active Record 对象传递给您的作业,而不是类/ID 对,后者您必须手动反序列化。以前,作业看起来像这样
class TrashableCleanupJob < ApplicationJob
def perform(trashable_class, trashable_id, depth)
trashable = trashable_class.constantize.find(trashable_id)
trashable.cleanup(depth)
end
end
现在您可以简单地这样做
class TrashableCleanupJob < ApplicationJob
def perform(trashable, depth)
trashable.cleanup(depth)
end
end
这适用于任何混入 GlobalID::Identification 的类,默认情况下已混入 Active Record 类。
10.2. 序列化器
您可以扩展支持的参数类型列表。您只需定义自己的序列化器
# app/serializers/money_serializer.rb
class MoneySerializer < ActiveJob::Serializers::ObjectSerializer
# Converts an object to a simpler representative using supported object types.
# The recommended representative is a Hash with a specific key. Keys can be of basic types only.
# You should call `super` to add the custom serializer type to the hash.
def serialize(money)
super(
"amount" => money.amount,
"currency" => money.currency
)
end
# Converts serialized value into a proper object.
def deserialize(hash)
Money.new(hash["amount"], hash["currency"])
end
private
# Checks if an argument should be serialized by this serializer.
def klass
Money
end
end
并将此序列化器添加到列表中
# config/initializers/custom_serializers.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer
请注意,初始化期间不支持自动加载可重新加载的代码。因此,建议设置序列化器只加载一次,例如,通过如下修改 config/application.rb
# config/application.rb
module YourApp
class Application < Rails::Application
config.autoload_once_paths << "#{root}/app/serializers"
end
end
11. 异常
作业执行期间引发的异常可以使用 rescue_from 处理
class GuestsCleanupJob < ApplicationJob
queue_as :default
rescue_from(ActiveRecord::RecordNotFound) do |exception|
# Do something with the exception
end
def perform
# Do something later
end
end
如果作业中的异常未被挽救,则该作业被称为“失败”。
11.1. 重试或丢弃失败的作业
除非另有配置,否则失败的作业将不会重试。
可以通过使用 retry_on 或 discard_on 来重试或丢弃失败的作业。例如
class RemoteServiceJob < ApplicationJob
retry_on CustomAppException # defaults to 3s wait, 5 attempts
discard_on ActiveJob::DeserializationError
def perform(*args)
# Might raise CustomAppException or ActiveJob::DeserializationError
end
end
11.2. 反序列化
GlobalID 允许序列化传递给 #perform 的完整 Active Record 对象。
如果在作业入队后但在调用 #perform 方法之前删除了一个传递的记录,Active Job 将引发 ActiveJob::DeserializationError 异常。
12. 作业测试
您可以在 测试指南 中找到有关如何测试作业的详细说明。
13. 调试
如果您需要帮助找出作业的来源,您可以启用 详细的入队日志。
14. 备用队列后端
Active Job 具有用于多个队列后端(Sidekiq、Resque、Delayed Job 等)的其他内置适配器。要获取适配器的最新列表,请参阅 ActiveJob::QueueAdapters 的 API 文档。
14.1. 配置后端
您可以使用 config.active_job.queue_adapter 更改您的队列后端
# config/application.rb
module YourApp
class Application < Rails::Application
# Be sure to have the adapter's gem in your Gemfile
# and follow the adapter's specific installation
# and deployment instructions.
config.active_job.queue_adapter = :sidekiq
end
end
您还可以按作业配置您的后端
class GuestsCleanupJob < ApplicationJob
self.queue_adapter = :resque
# ...
end
# Now your job will use `resque` as its backend queue adapter, overriding the default Solid Queue adapter.
14.2. 启动后端
由于作业与您的 Rails 应用程序并行运行,大多数队列库要求您启动一个库特定的队列服务(除了启动您的 Rails 应用程序之外),才能使作业处理正常工作。请参阅库文档以获取有关启动队列后端的说明。
以下是一个不全面的文档列表