rufus-scheduler looks good

rufus-scheduler,ジョブのスケジューリングをシンプルに行える。
類似のところでwheneverというgemがあるが,そちらではcronで実行するジョブをわかりやすいrubyシンタックスで記述できcronに反映させるが,rufus-schedulerはシンプルなスレッドとして実装されている。cronというメソッドもあるが,cronには影響しない。

シンプルなスレッドの実装であり,プロセスが終了するとschedulerも終了するので,バックグラウンドで動かすにはデーモン化する。試しにこんな感じで書いてみる。

require 'rufus-scheduler'
begin
  puts "daemonize..."
  Process.daemon
end if ARGV.include?("--daemon")
scheduler = Rufus::Scheduler.new(
    lockfile: "/tmp/rufus-scheduler.pid",
)
puts "#{scheduler.started_at} start scheduler"
Signal.trap(:INT) do |signo|
  puts "just get a signal #{signo}, so shutdown now..."
  scheduler.shutdown :wait
  puts "ok"
  exit
end
# in
job =
    scheduler.in '5s', :job => true do |job, time|
      puts "#{Time.now}, #{time} schedule in"
    end
puts job
# at
scheduler.at '2015/07/11 02:10:00' do |job, time|
  puts "#{Time.now}, #{time} schedule at"
end
# every
scheduler.every '5s', :overlap => true, :myarg => 'myarg'  do |job, time|
  job[:counter] = if job[:counter].nil?
                    1
                  else
                    job[:counter] + 1
                  end
  puts "#{Time.now}, #{time} schedule every, #{job[:counter]}, opts: #{job.opts}"
  if job[:counter] >= 5
    puts "#{Time.now}, #{time} stop schedule every"
    puts "mean work time: #{job.mean_work_time}"
    job.unschedule
  end
  sleep 6
  puts "#{Time.now} schedule every done"
end
# interval
scheduler.interval '5s' do |job, time|
  puts "#{Time.now}, #{time} schedule interval"
  sleep 6
  puts "#{Time.now} schedule interval done"
end
# per 1 minute
scheduler.cron "*/1 * * * *", :timeout => '10s' do |job, time|
  puts "#{Time.now}, #{time} schedule cron"
  begin
    sleep 15
  rescue Rufus::Scheduler::TimeoutError
    puts "#{Time.now}, #{time} timeout #{job}"
  end
end
scheduler.every '60s', :overlap => false, :tag => :impala do |job, time|
#require 'impala'
#Impala.connect('impala_host', 21000) do |conn|
#  conn.execute('refresh access_log')
#end
  puts "#{Time.now}, #{time} refresh access_log"
  sleep 2
end
scheduler.jobs(:tag => :impala).each {|job| puts "#{job.id} #{job.opts}" }
scheduler.join

実行すると以下のようになる。

$ bundle exec ruby rufus-scheduler-sample.rb
2015-07-12 00:09:15 +0900 start scheduler #<Rufus::Scheduler::InJob:0x007ff55190ec70>
every_1436627355.0505002_383478777958208319 {:overlap=>false, :tag=>:impala}
2015-07-12 00:09:15 +0900, 2015-07-12 00:09:15 +0900 schedule at
2015-07-12 00:09:20 +0900, 2015-07-12 00:09:20 +0900 schedule in
2015-07-12 00:09:20 +0900, 2015-07-12 00:09:20 +0900 schedule every, 1, opts: {:overlap=>true, :myarg=>"myarg"}
2015-07-12 00:09:20 +0900, 2015-07-12 00:09:20 +0900 schedule interval
2015-07-12 00:09:25 +0900, 2015-07-12 00:09:25 +0900 schedule every, 2, opts: {:overlap=>true, :myarg=>"myarg"}
2015-07-12 00:09:26 +0900 schedule interval done
2015-07-12 00:09:26 +0900 schedule every done
2015-07-12 00:09:30 +0900, 2015-07-12 00:09:30 +0900 schedule every, 3, opts: {:overlap=>true, :myarg=>"myarg"}
2015-07-12 00:09:31 +0900 schedule every done
2015-07-12 00:09:31 +0900, 2015-07-12 00:09:31 +0900 schedule interval
2015-07-12 00:09:35 +0900, 2015-07-12 00:09:35 +0900 schedule every, 4, opts: {:overlap=>true, :myarg=>"myarg"}
2015-07-12 00:09:36 +0900 schedule every done
2015-07-12 00:09:37 +0900 schedule interval done
2015-07-12 00:09:40 +0900, 2015-07-12 00:09:40 +0900 schedule every, 5, opts: {:overlap=>true, :myarg=>"myarg"}
2015-07-12 00:09:40 +0900, 2015-07-12 00:09:40 +0900 stop schedule every
mean work time: 4.50305275
2015-07-12 00:09:41 +0900 schedule every done
2015-07-12 00:09:42 +0900, 2015-07-12 00:09:42 +0900 schedule interval
2015-07-12 00:09:46 +0900 schedule every done
2015-07-12 00:09:48 +0900 schedule interval done
2015-07-12 00:09:53 +0900, 2015-07-12 00:09:53 +0900 schedule interval
2015-07-12 00:09:59 +0900 schedule interval done
2015-07-12 00:10:00 +0900, 2015-07-12 00:10:00 +0900 schedule cron
2015-07-12 00:10:05 +0900, 2015-07-12 00:10:05 +0900 schedule interval
2015-07-12 00:10:10 +0900, 2015-07-12 00:10:00 +0900 timeout #<Rufus::Scheduler::CronJob:0x007ff551357a48>
2015-07-12 00:10:11 +0900 schedule interval done
2015-07-12 00:10:15 +0900, 2015-07-12 00:10:15 +0900 refresh access_log
2015-07-12 00:10:16 +0900, 2015-07-12 00:10:16 +0900 schedule interval
^Cjust get a signal 2, so shutdown now...
ok

lockfileの中身。

$ cat /tmp/rufus-scheduler.pid
pid: 47122, scheduler.object_id: 70345803096920, time: 2015-07-12 00:09:15 +0900, timestamp: 1436627355.0475981

バックグラウンドで実行。

$ bundle exec ruby rufus-scheduler-sample.rb --daemon

in, atは,非repeatableなジョブ。repeatableなジョブは,everyやinterval,cronになる。
everyとintervalの違いは,everyはジョブの開始時間を起点にスケジュールし,intervalはジョブ終了時間から次の開始時間までの間隔でスケジュールする(jobs.rbより)。

mutexを使って関連するジョブの制御もできる。

require 'rufus-scheduler'
scheduler = Rufus::Scheduler.new
puts "#{scheduler.started_at} start scheduler"
$mutex = Mutex.new
scheduler.every '5s', :mutex => $mutex do |job, time|
  puts "#{Time.now}, #{time} every 5s"
end
scheduler.interval '3s', :mutex => $mutex do |job, time|
  puts "#{Time.now}, #{time} interval 3s"
  sleep 5
end
scheduler.join

実行すると以下。

2015-07-12 00:12:33 +0900 start scheduler
2015-07-12 00:12:36 +0900, 2015-07-12 00:12:36 +0900 interval 3s
2015-07-12 00:12:41 +0900, 2015-07-12 00:12:38 +0900 every 5s
2015-07-12 00:12:43 +0900, 2015-07-12 00:12:43 +0900 every 5s
2015-07-12 00:12:44 +0900, 2015-07-12 00:12:44 +0900 interval 3s
2015-07-12 00:12:49 +0900, 2015-07-12 00:12:48 +0900 every 5s
2015-07-12 00:12:52 +0900, 2015-07-12 00:12:52 +0900 interval 3s
2015-07-12 00:12:57 +0900, 2015-07-12 00:12:53 +0900 every 5s
2015-07-12 00:12:58 +0900, 2015-07-12 00:12:58 +0900 every 5s
2015-07-12 00:13:00 +0900, 2015-07-12 00:13:00 +0900 interval 3s
2015-07-12 00:13:05 +0900, 2015-07-12 00:13:03 +0900 every 5s
2015-07-12 00:13:08 +0900, 2015-07-12 00:13:08 +0900 interval 3s
2015-07-12 00:13:13 +0900, 2015-07-12 00:13:09 +0900 every 5s
2015-07-12 00:13:14 +0900, 2015-07-12 00:13:14 +0900 every 5s
2015-07-12 00:13:16 +0900, 2015-07-12 00:13:16 +0900 interval 3s
2015-07-12 00:13:21 +0900, 2015-07-12 00:13:19 +0900 every 5s
2015-07-12 00:13:24 +0900, 2015-07-12 00:13:24 +0900 every 5s
2015-07-12 00:13:25 +0900, 2015-07-12 00:13:25 +0900 interval 3s
^C

他にも,色々とオプションがある(以下のREADMEを参照)

参考:jmettraux/rufus-scheduler · GitHub

  • 追記 2015-7-25

rakeと合わせると使いやすいかもしれない。

require "thor"
require "rufus-scheduler"
require "rake"


task :hello do |t|
  t.reenable
  puts "#{Time.now}"
end

class TaskRunner < Thor

  desc "exec TASK", "run tasks"
  def exec(task)
    Rake.application[task].invoke
  end

  desc "start_scheduler", "run a job scheduler"
  def start_scheduler
    STDOUT.puts "==> start scheduler"
    scheduler = Rufus::Scheduler.new
    scheduler.interval "5s" do
      Rake.application["hello"].invoke
    end
    STDOUT.puts "waiting..."
    scheduler.join
  end
end

TaskRunner.start(ARGV)

byebyehaikikyou

日記やIT系関連のネタ、WordPressに関することなど様々な事柄を書き付けた雑記です。ITエンジニア経験があるのでプログラミングに関することなどが多いです。

シェアする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

コメントする