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
runner 'WeeklyReportJob.perform_later'
end
三种任务类型:
rake:执行 Rake 任务(最常用的收口方式,逻辑写在lib/tasks/)。runner:跑一段 Rails 环境内的 Ruby 代码。command:裸 shell 命令;ActiveJob 用runner调用perform_later,并配置相应队列 worker。
3. 写入 crontab
whenever --update-crontab # 编译并写入当前用户 crontab
crontab -l # 查看生成结果
生成的规则带 whenever 的注释标记,下次更新会精确替换自己管理的段落,不影响其他条目。
4. 部署集成
Capistrano 3 在 Capfile 中加入 require 'whenever/capistrano',在 config/deploy.rb 中配置:
set :whenever_command, 'bundle exec whenever'
每次部署自动刷新 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 之类辅助解析,但调度时区取决于 cron 实现与配置;容器里的 TZ 环境变量不保证守护进程采用该时区,需按实现验证。
Q:能从 crontab 里移除 whenever 管理的条目吗?
A:bundle exec whenever --clear-crontab 清除对应标识的受管段落;添加时用了自定义标识,清除时也要传同一标识。