ぴよログ

↓に移転したのでこっちは更新されません、多分。

Rubyでcronのような定期実行を実現するclockwork

移転しました →

先日の記事でもちょろっと紹介しましたが、RSSリーダーを開発した際にフィード更新ジョブを定期的に発行するためにclockworkというgemを使用しました。

RSSリーダーの記事更新のバックグラウンドジョブ概要 - PILOG

単独で使う

まずはRuby単独で使う方法です。ローカルマシンで定期的にスクリプトを動かしたいときなどに使えます(そんなときあるか!?)。

まずgemを導入します。普通にgem installでいけます。

$ gem install clockwork

次にコンフィグファイル的なものを書きます。例えばclock.rbというような名前で作るとします。

require 'clockwork'

module Clockwork

  def one_minute
    p "minutely job"
  end

  def one_hour
    p "hourly job"
  end


  handler do |job|
    self.send(job.to_sym)
  end

  every(1.minute, 'one_minute')
  every(1.hour, 'one_hour')
end

最後の2行で1分ごとのジョブと1時間ごとのジョブを設定しています。そしてhandlerに処理記述します。この例ではジョブ名と同じメソッドを定義しておいて、sendによってそちらを呼ぶようにしています。

フォアグラウンド実行

フォアグラウンド実行を行うときは次のように実行します。

$ clockwork clock.rb
I, [2014-02-16T11:11:17.128897 #97926]  INFO -- : Starting clock for 2 events: [ one_minute one_hour ]
I, [2014-02-16T11:11:17.129034 #97926]  INFO -- : Triggering 'one_minute'
"minutely job"
I, [2014-02-16T11:11:17.129107 #97926]  INFO -- : Triggering 'one_hour'
"hourly job"
I, [2014-02-16T11:12:17.182378 #97926]  INFO -- : Triggering 'one_minute'
"minutely job"
I, [2014-02-16T11:13:17.231925 #97926]  INFO -- : Triggering 'one_minute'
"minutely job"

実行すると同時に1時間毎のジョブと1分ごとのジョブが実行され、それ以降は指定した間隔でジョブが動いていきます。

atオプションを使うことでcronのように◯時XX分に実行する、というような指定も可能です。

every(1.hour, 'hourly', :at => '**:30')

バックグラウンド実行(デーモン)

定期実行という機能の特性を考えるとバックグラウンドで実行したいケースがほとんどだと思います。そのためにはclockworkと一緒についてくるclockworkdを使います。

$ clockworkd -c clock.rb start --log

-cはコンフィグファイルを指定というようなニュアンスだと思われます。clock.rbファイルを指定してstartすればバックグラウンドで起動します。--logオプションを付けることにより、フォアグラウンド実行のとき標準出力されていたログがファイルに出るようになります。

Railsと使う

アプリケーションと一緒に使うことでより実用的になります。Railsの場合はclockworkをrequireしたあとでRailsの環境をrequireすることでRailsのコードを使えるようになります。

Railsの環境を読み込むにはconfig/bootconfig/environmentをrequireします。requireする際のパスに注意して書く必要があるのですが、clockworkのコンフィグファイルがbootなどと同じくconfigにある場合は、

require File.expand_path('../boot', __FILE__)
require File.expand_path('../environment', __FILE__)

などと書きます。

これをやっておくことでRailsのモデルなどにアクセスできるようになって、より実用的な使い方をできるようになりますね。

require 'clockwork'

require File.expand_path('../boot', __FILE__)
require File.expand_path('../environment', __FILE__)

module Clockwork

  handler do |job|
    p "#{job}: user count => #{User.count}"
  end

  every(1.minute, 'minute')
end