Rails 中如何运行定时(cron)任务?
Rails 跑定时任务的标准方案:whenever gem 用 Ruby 语法定义调度并生成 crontab;附 schedule.rb 示例、部署更新命令、rake runner/job 三种任务类型与 FAQ。
用 whenever gem:在 config/schedule.rb 里用 Ruby DSL 写调度,whenever --update-crontab 把它编译成系统 crontab。 调度定义跟代码一起进版本库、随部署更新,比在服务器上手改 crontab 可靠得多。
1. 安装
# Gemfile
gem 'whenever', require: false
bundle install
bundle exec wheneverize . # 生成 config/schedule.rb
2. 定义调度(config/schedule.rb)
# 每天凌晨 2 点清理过期会话
every 1.day, at: '2:00 am' do
rake 'sessions:cleanup'
end
# 每 10 分钟同步一次数据
every 10.minutes do
runner 'SyncService.call'
end
# 每周一生成报表(ActiveJob)
every :monday, at: '6:30 am' do
job 'WeeklyReportJob.perform_later'
end
三种任务类型:
rake:执行 Rake 任务(最常用的收口方式,逻辑写在lib/tasks/)。runner:跑一段 Rails 环境内的 Ruby 代码。command/job:裸 shell 命令或 ActiveJob。
3. 写入 crontab
whenever --update-crontab # 编译并写入当前用户 crontab
crontab -l # 查看生成结果
生成的规则带 whenever 的注释标记,下次更新会精确替换自己管理的段落,不影响其他条目。
4. 部署集成
Capistrano 在 deploy.rb 里加:
set :whenever_command, 'bundle exec whenever'
require 'whenever/capistrano'
每次部署自动刷新 crontab。其他部署方式(Docker/K8s)则注意:只有一份实例该跑 whenever 更新,多副本同时注册同一批任务会重复执行——常见做法是单独一个 "scheduler" 角色容器。
更新调度后,检查下一次任务的开始、结束和退出结果。Rails 日志与任务输出写入文件时,可按 DataKit 日志采集汇入观测云,用任务名和执行时间查找异常堆栈;仅有 cron 触发记录还不能证明任务成功。
常见问题(FAQ)
Q:改了 schedule.rb 后任务没变?
A:schedule.rb 只是源文件,必须重跑 whenever --update-crontab 才会写进系统 crontab。
Q:服务器上任务的时区不对?
A:whenever 用的是系统时间。在 schedule.rb 顶部可设 set :chronic_options, hours24: true 之类辅助解析,但时区本身跟着 OS 走——Docker 镜像里记得设 TZ。
Q:能从 crontab 里移除 whenever 管理的条目吗?
A:whenever --clear-crontab 清除它写入的全部条目。