Rails 定时任务——whenever实现周期性任务
根据项目的进展,我们需要实现后台进行定时读取信息的功能,而最关键的实现部分是周期性功能,根据调研,决定使用whenever来实现这一功能。
github:https://github.com/javan/whenever
开发前需要明确的问题
- whenever是怎样一种周期性机制?
- whenever能为我们提供什么功能?
- whenever为周期性任务提供了哪些控制方式?
问题解决
whenever周期性机制
我们来看一下github上面是怎么说的:
Whenever is a Ruby gem that provides a clear syntax for writing and deploying cron jobs.
意思就是说,whenever是一个ruby gem,但同时它是基于cron jobs的。
那么什么是cron jobs呢?我们来看一下维基百科的定义:
Cron
crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行。该词来源于希腊语chronos(χρόνος),原意是时间。
通常,crontab储存的指令被守护进程激活,crond常常在后台运行,每一分钟检查是否有预定的作业需要执行。这类作业一般称为cron jobs。
也就是说,crontab是在unix和类unix系统中用来实现周期性功能的指令。在网上搜一下,我们就会看到很多crontab指令相关的语法。
根据上述的分析,我们可以得出这样的结论:
whenever事实上是一个cron翻译器,它将rails中的ruby代码翻译成cron脚本,从而将周期性的任务交给cron来执行。 这样,通过whenever我们可以使用ruby语言来写周期性任务代码,在ruby层控制代码,而不需要与shell脚本进行切换;另一方面,我们会发现,由于cron命令的强大,它的语法也因此变得很复杂,通过whenever,我们可以很方便的实现周期性任务。
whenever功能
从github中我们可以看到,我们用来实现定时功能的代码都已经集成在config/schedule.rb
文件中了,以下为github中的whenever实例:
every 3.hours do
# 1.minute 1.day 1.week 1.month 1.year is also supported
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 :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为我们提供的三种方法,runner
、rake
和command
,事实上,还有script
方法也是whenever默认支持的。除了这四种方法外,wheneverr也提供自定义方法的功能,具体做法参照github上的方法来做即可。
whenever控制方法
whenever是十分亲民的,它的指令没有cron命令那么复杂。下面提供几种whenever比较常用的方法:
whenever (不带参数)将schedule.rb 文件中的周期性任务转化成cron命令显示出来,但不读取或者写入到crontab文件中
whenever -i [identifier] 更新新定时任务,默认更新 schedule.rb 文件中的全部
whenever -w, --write-crontab, --update-crontab, [identifier] 写定时任务,默认更新 schedule.rb 文件中的全部
whenever -c, --clear-crontab [identifier] 清除 crontab
除此之外,cron命令也是可以是用的,比较常用的是:
crontab -l [UserName]: 列出目前的时程表
crontab -e [UserName]: 执行文字编辑器(第一次由自己选择编辑器)来配置crontab的具体执行方法
一个十分简单的whenever demo
1.添加whenever
(Gemfile
)
gem 'whenever', :require => false
2.生成config/schedule.rb
文件
执行命令:
wheneverize
3.添加自己的周期性任务
在config/schedule.rb
文件中添加:
set :environment, :development
every 2.minutes do
runner "Timetest.mytime"
end
其中,set :environment, :development
是设置执行任务时的环境,默认情况下环境为production
上述代码实现的是每两分钟读取当前时间并存入到数据库的功能。其中,runner
方法执行的方法如下:
class Timetest < ApplicationRecord
def self.mytime
a = Timetest.new
a.time_now = Time.now
a.save
end
end
这样,在rails中实现whenever的代码就算是写完了,真的是简单到不行啊!(实在忍不住感慨一句)
下面就要执行周期性任务了。
4.执行周期性任务
在rails工程文件夹下进行一下操作
- 更新schedule.rb中的任务到cronjob中
whenever -i
可以看到这样的打印结果:
[write] crontab file updated
- 执行周期性任务
whenever -w
可以看到:
[write] crontab file written
此时我们的周期性任务便在后台运行了,此时查看我们的任务:
crontab -l
可以看到以下打印:
# Begin Whenever generated tasks for: /home/vito/rails/test_of_rails/test_rails/config/schedule.rb
0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58 * * * * /bin/bash -l -c 'cd /home/vito/rails/test_of_rails/test_rails && bundle exec bin/rails runner -e development '\''Timetest.mytime'\'''
# End Whenever generated tasks for: /home/vito/rails/test_of_rails/test_rails/config/schedule.rb
这样,我们的周期性任务就算是在顺利执行了。
需要注意的一点是运行时crontab的环境(rails和crontab环境不匹配时whenever无法执行),一般调试时多使用的是development
环境,而不设置时默认的是production环境,如果你使用crontab -l
发现是production环境,可以使用
crontab -e
直接修改为development
,或者直接将-e production
删掉即可。
经过上述流程,我们便可以成功地实现周期性任务了。如果此时你发现自己的周期性任务还是没有执行,那你就得好好看看你自己的任务代码了,很可能是执行的任务代码本身有问题,而与whenever的实现没有太大的关系了