Whenever 是一个 Ruby gem,它提供了清晰的语法用于编写和部署 cron jobs。本文翻译自它的 Github:https://github.com/javan/when...
安装
$ gem install whenever
或者写到 Gemfile,配合Bundle:
gem 'whenever', require: false
开始
$ cd /apps/my-great-project
$ bundle exec wheneverize .
为你创建初始化配置文件 config/schedule.rb
(只要你的目录下有 config 文件夹)。
whenever 命令
whenever
命令只是将 schedule.rb
文件的内容输出为 cron 语法结构,并不会读取和写入你的 crontab 文件。
$ cd /apps/my-great-project
$ bundle exec whenever
如果要将这些任务写入到 crontab 文件中去,带参数执行命令:
$ whenever --update-crontab
其他常用命令:
$ whenever --user app # set a user as which to install the crontab
$ whenever --load-file config/my_schedule.rb # set the schedule file
$ whenever --crontab-command 'sudo crontab' # override the crontab command
提示:如果你运行 whenever --update-crontab 没有附带 --user 选项,所生成的 cron 将只能被当前用户执行。这意味着如果你所执行的任务需要其他用户的权限,将失败。
可以使用 crontab -l
列出当前当前系统的任务列表。
运行 whenever --help
,输出一些用于调度的选项、调度变量以及其他内容。
schedule.rb 文件例子
every 3.hours do # 1.minute 1.day 1.week 1.month 1.year is also supported
# the following tasks are run in parallel (not in sequence)
runner "MyModel.some_process"
rake "my:rake:task"
command "/usr/bin/my_great_command"
end
every 1.day, at: '4:30 am' do
runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
end
every 1.day, at: ['4:30 am', '6:00 pm'] do
runner "Mymodel.task_to_run_in_two_times_every_day"
end
every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
runner "SomeModel.ladeeda"
end
every :sunday, at: '12pm' do # Use any day of the week or :weekend, :weekday
runner "Task.do_something_great"
end
every '0 0 27-31 * *' do
command "echo 'you can use raw cron syntax too'"
end
# run this task only on servers with the :app role in Capistrano
# see Capistrano roles section below
every :day, at: '12:20am', roles: [:app] do
rake "app_server:task"
end
定义自己的工作类型
Whenever 内置三种类型的工作:command,runner 以及 rake。你可以通过定义job_type
,来定义你自己的工作类型。
举个例子:
job_type :awesome, '/usr/local/bin/awesome :task :fun_level'
every 2.hours do
awesome "party", fun_level: "extreme"
end
这段例子的意思是,每两个小时,执行一次 /usr/local/bin/awesome party extreme
。:task
总是作为第一个参数,额外的参数被替换其他传递的内容,或者使用 set
定义的变量。
whenever 中,默认的工作类型定义如下:
job_type :command, ":task :output"
job_type :rake, "cd :path && :environment_variable=:environment bundle exec rake :task --silent :output"
job_type :runner, "cd :path && bin/rails runner -e :environment ':task' :output"
job_type :script, "cd :path && :environment_variable=:environment bundle exec script/:task :output"
Rails 3 之前的应用程序,和不使用 Bundler 的应用程序,将分别重新定义 rake 和runner,以使其正常运行。
如果 :path
没有设置,使用当前whenever
执行的目录。:environment_variable
变量默认使用 RAILS_ENV
。:environment
默认设置为production
。:outpuot
用于配置你的输出重定向,可以在(这里)[http://github.com/javan/whene...],了解到如何配置。
所有的工作默认通过 bash -l -c 'command'
来执行。这会使你的 cron job 加载整个环境变量,而不是 cron 执行的有限环境。这让你的 rvm 工作的更好。了解更多:http://github.com/javan/whene...
你可以通过定义 :job_template
来改变这一默认行为。
set :job_template, "bash -l -c ':job'"
设置 :job_setemplate
为空,来恢复标准行为:
set :job_template, nil
解析日期和时间
Whenever 使用 (Chronic)[https://github.com/mojombo/ch...] gem,来解析指定的日期和时间。
如果默认的 Chronic 配置不适合你,你可以自定义。
举一个例子,使用24小时制,来替换默认的12小时制:
set :chronic_options, hours24: true
# By default this would run the job every day at 3am
every 1.day, at: '3:00' do
runner "MyModel.nightly_archive_job"
end
可以在这里找到配置选项:
https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb
通过 MAILTO
环境变量,自定义 email 接收对象
工作的结果,可以输出到电子邮件地址,通过 MAILTO
环境变量来配置。
多种方式可以配置邮件接受地址:
全局配置例子:
env 'MAILTO', '[email protected]'
every 3.hours do
command "/usr/bin/my_great_command"
end
只在一个代码块中生效的例子:
every 3.hours, mailto: '[email protected]' do
command "/usr/bin/my_super_command"
end
只在一个工作中生效的例子:
every 3.hours do
command "/usr/bin/my_super_command", mailto: '[email protected]'
end
Capistrano 集成
使用内置的 Capistrano recipe,即可完成简单的 crontab 部署时更新。 Capistrano V3 的使用,将在下一节介绍。
打开 "config/deploy.rb" 文件:
require "whenever/capistrano"
在这里https://github.com/javan/whenever/blob/master/lib/whenever/capistrano/v2/recipes.rb 你可以找到 recipe 可以使用的配置项。比如你想使用 bundler:
set :whenever_command, "bundle exec whenever"
require "whenever/capistrano"
如果你想在不同的环境中使用(如 staging,production),你可以这么用:
set :whenever_environment, defer { stage }
require "whenever/capistrano"
capistrano 变量 :stage 保存着当前环境的名称。这将在 schedule.rb 文件中提供正确的 :environment。
如果你的两个环境,在同一台服务器上,你需要为他们提供命名空间,否则将在部署时相互覆盖:
set :whenever_environment, defer { stage }
set :whenever_identifier, defer { "#{application}_#{stage}" }
require "whenever/capistrano"
如果你要在别的路径开启调度,你需要这样配置:
set :whenever_load_file, defer { "#{release_path}/somewhere/else/schedule.rb" }
require "whenever/capistrano"
Capistrano V3 Integration
打开你的 "Capfile" 文件:
require "whenever/capistrano"
查看文件底部的 load:default task 以了解可以配置的选项。举个例子,crontab 的 application 或 stage 命名空间条目的行为,在 "config/deploy.rb" 文件中配置。
set :whenever_identifier, ->{ "#{fetch(:application)}_#{fetch(:stage)}" }
集成 Capistrano,默认期望 :application
变量被设置,以便在 crontab 中确定作业范围。
Capistrano 权限
关于权限,首先要知道的是它是完全可选的,以及向后兼容。如果你不需要 Crontabs 部署不同的工作,在不同的服务器上,你可以停止阅读,并放心的跳过这块的内容,一切都工作的很好。
当你在 schedule.rb 文件中定义工作的时候,默认它会被分发到所有在 whenever_roles 列表中的服务器。(该列表默认为 [:db]
):
very :day, at: '12:20am' do
rake 'foo:bar'
end
如果我们在 deploy.rb 中,设置 whenever_roles 为 [:db, :app],以及 schedule.rb 中加入工作:
every :day, at: '1:37pm', roles: [:app] do
rake 'app:task' # will only be added to crontabs of :app servers
end
every :hour, roles: [:db] do
rake 'db:task' # will only be added to crontabs of :db servers
end
every :day, at: '12:02am' do
command "run_this_everywhere" # will be deployed to :db and :app servers
end
简单的规则:
- 如果 whenever_roles 中,未列出服务器的角色,则永远不会将作业添加到他的 crontab 中。
- 如果 whenever_roles 中,包含服务器的角色,那么将在其 crontab 中添加所有作业,如果包含 :roles 参数,这些作业将被列出。
- 如果作业有 :roles 参数,但角色不在 whenever_roles 列表中,则作业不会被部署到任何服务器上
RVM Integration
如果你的产品生产环境,使用 RVM(Ruby Version Manager),你将会遇到一个导致你的 cron job 挂起的陷阱。这与 whenever 并无直接关联,调试起来很麻烦。你的 .rvmrc 文件必须被可信,否则 cron jobs 将挂起等待该文件受信任。一个解决办法是,将这一行添加到 ~/.rvmrc 中,来禁止这个提示。
rvm_trust_rvmrcs_flag=1
这表示 rvm 将信任所有的 rvmrc 文件。
Heroku?
Heroku 不支持 cron,作为替代,提供了Heroku Scheduler。如果你要部署到 Herku,你要使用它,而不是 Whenever。
Testing
whenever-test 是一个 Whenever 的扩展,用于测试 Whenever schedule。
Credit
Whenever 的诞生,是为了使用 Inkling(http://inklingmarkets.com/)。可以在这里进行了解:http://blog.inklingmarkets.co...。
谢谢所有贡献者,是他们让 Whenever 变得更好:http://github.com/javan/whene...
讨论/反馈/提问/Bugs
一般的讨论和问题,请访问 google group:http://groups.google.com/group/whenever-gem
如果你遇到大 bug 或者问题,可以使用 github 的 issue:http://github.com/javan/whene...
Ryan Bates 创建了一份很好的关于 Whenever 的 Railscast:http://railscasts.com/episode... 可能有些过时,但是个很好的开始。