GoodJob 实战:基于 PostgreSQL 的 Rails 后台任务处理

GoodJob 是直接用 PostgreSQL 驱动的 Rails 后台任务框架,无需 Redis 即可实现异步任务、多队列优先级与定时任务。本文从零搭建 GoodJob 并讲解任务创建、队列配置与生产监控。

最佳实践
任务队列与工作节点调度插画

GoodJob 是一个跑在 Rails 引擎里的多线程后台任务处理器,直接复用你现有的 PostgreSQL 数据库(靠 advisory lock 和 LISTEN/NOTIFY 实现任务领取与即时执行),不需要额外部署 Redis。中小型 Rails 应用选它可以把基础设施减到最少,同时获得 Web 仪表盘、定时任务和优先级队列。

为什么选择 GoodJob

传统方案 Sidekiq 依赖 Redis 做队列存储,意味着多一套中间件要部署、监控、备份。GoodJob 的思路是:既然 PostgreSQL 已经在跑了,事务、锁、通知机制都是现成的,为什么不能直接当队列用?对于每秒任务量几十到几百的应用(覆盖了绝大多数业务系统),Postgres 完全扛得住,架构却简单了一大截。

安装配置

bundle add good_job
rails g good_job:install
rails db:migrate

迁移会创建 good_jobs 等表。然后在 config/application.rb 指定适配器:

config.active_job.queue_adapter = :good_job

开发环境默认内嵌执行(随 Web 进程跑),生产环境建议独立进程:

bundle exec good_job start

创建第一个任务

任务类基于 ActiveJob:

class ReportMailerJob < ApplicationJob
  queue_as :mailers
  retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 5

  def perform(report_id)
    report = Report.find(report_id)
    ReportMailer.with(report: report).deliver_now
  end
end

入队执行:

ReportMailerJob.perform_later(report.id)                    # 立即异步执行
ReportMailerJob.set(wait: 10.minutes).perform_later(r.id)   # 延迟执行

注意 perform_later 传参走序列化,传 id 不传对象——任务执行时对象可能已变化,而且大对象序列化进库既不安全也低效。

多队列与优先级

按业务划分队列,资源分配互不干扰:

# config/application.rb
config.good_job.queues = {
  critical: 4,    # 4 个线程
  default: 2,
  mailers: 2
}

队列名的顺序即优先级;也可以配置 :critical => 1, :default => 1 让 critical 队列优先抢占线程。任务的 queue_as 声明落入哪个队列,紧急任务(支付回调)和普通任务(报表生成)从此各走各道。

定时任务(Cron)

GoodJob 内置 cron 式调度,不需要额外的 whenever 或系统 crontab:

config.good_job.enable_cron = true
config.good_job.cron = {
  daily_report: {
    cron: '0 8 * * *',
    class: 'DailyReportJob',
    description: '每天早 8 点生成经营日报'
  }
}

调度记录同样存在 Postgres 里,多实例部署时靠数据库锁保证不重复触发。

Web 仪表盘

挂载引擎即可获得任务管理界面:

# config/routes.rb
mount GoodJob::Engine => 'good_job'

可以查看队列深度、失败任务、重试历史、手动重跑。生产环境务必加认证约束(如 authenticate :admin),别把仪表盘裸奔到公网。

辨别 GoodJob 积压与数据库争用

GoodJob 使用 PostgreSQL 存储任务,可把队列等待、任务失败日志与数据库连接和锁等待放在同一时间窗口分析。观测云采集数据库指标后,应用仍需另行输出队列深度、排队时长和任务结果;这些不是普通 PostgreSQL 采集器自动提供的业务指标。若要追踪某次任务中的 SQL 和外部调用,再为 Ruby Worker 配置兼容的 APM 插桩。

接入文档:PostgreSQL 采集 · 日志采集 · Ruby APM 接入 · 监控器与告警策略。

常见问题(FAQ)

Q:GoodJob 性能扛得住吗?会不会拖慢主库?

常规业务量级(每分钟数千任务内)完全没问题,GoodJob 的轮询和锁设计对主库压力很小。任务表会持续增长,建议定期清理已完成的旧记录(自带 good_job cleanup 任务)。超高吞吐场景(每秒上万任务)再评估专用队列。

Q:任务执行到一半进程被杀会怎样?

Postgres 的 advisory lock 会随连接断开自动释放,未完成的任务会被其他 worker 重新认领执行——这正是基于数据库的可靠性优势。但任务本身要设计成幂等的(重复执行不出错),这是所有后台任务框架的通用要求。

Q:能和 Sidekiq 共存吗?

可以渐进迁移:不同队列用不同适配器(config.active_job.queue_adapter 可按任务类覆盖),先把新任务切到 GoodJob,存量任务跑完再下线 Redis。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台