ぱろっと・すたじお

技術メモなどをまったりと / my site : http://parrot-studio.com/

Rubyのdaemonを手軽に作る

今回も基本的には個人的なメモ書き...φ(・ω・`)


時間のかかる処理を非同期化して動かすって手段(gem/plugin等)はいろいろありまして、
そういうのを知らなかった頃、自前でタスク処理フレームワーク*1を組んだりしましたが、
それは置いておいて・・・


そこまでいかなくても、何かを単純な処理を実行しっぱなしにしたい、
つまりdaemon化したいってのは良くある話でして


そんな人のために、Ruby1.9系には「Process.daemon」という、
そのものズバリのメソッドが用意されています


ただ、こいつは「単純なdaemon*2」しかしません
pidファイルの処理や、出力をlogへリダイレクトなんて処理は、
自前で書かなければなりません(´-ω-)


それも面倒なので、軽量な仕組みはないかな・・・と探したところ、
daemon-spawn」というgemを見つけました


https://github.com/alexvollmer/daemon-spawn


使い方はREADMEにもありますが、
短いコードなので読んじゃった方が早いかもしれません


以下、Rails3アプリに組み込むことを想定しますが、
例によってGemfileにgemを追加・・・とはいきませんΣ(・ω・ノ)ノ


このgem、名前は「daemon-spawn」なのですが、
requireファイル名は「daemon_spawn」なのです


そこで、こう書きます

gem 'daemon-spawn', :require => 'daemon_spawn'

後はいつものようにbundle installでOKです


続いて、daemon化したい処理を書きます

# coding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../config/environment')
# rails runner前提だとこのrequireいらないかも

class MyDaemon < DaemonSpawn::Base
  def start(args)
    # argsにはARGVがそのまま渡されてくる

    puts "start : #{Time.now}"

    # 何か終わらない処理
  end

  def stop
    puts "stop  : #{Time.now}"

    # やることがなくても、メソッドを実装しないと例外
  end
end

MyDaemon.spawn!({
    :working_dir => Rails.root, # これだけ必須オプション
    :pid_file => File.expand_path(File.dirname(__FILE__) + '/../tmp/hoge.pid'),
    :log_file => File.expand_path(File.dirname(__FILE__) + '/../log/hoge.log'),
    :sync_log => true,
    :singleton => true # これを指定すると多重起動しない
  })

DaemonSpawn::Baseを継承したクラスに、
startとstopを実装し、spawn!を呼ぶだけです


startの中身がすぐ終わるものだとdaemonも終了してしまうので、
処理をloopで囲むなりなんなりしてください
(もっとも、これを使おうと思った時点でそれに近い実装だと思いますが)


ここまでやれば、start/stop/restart/statusを渡して起動するだけです
以前、unicorn_railsで引っかかったので、念のためbundle exec経由で
rails runnerでもいけます

$ cd RAILS_ROOT
$ bundle exec ruby script/my_daemon start
$ rails runner script/my_daemon stop

「:singleton => true」を指定しているので、
cronで数分おきに起動してやれば、生きていれば何もしませんし、
何らかの事情で落ちていればまた復活します(`・ω・´) b *3



基本的にはこれでおしまいなのですが、
本番で動かそうとしてはまったのでもうちょっと


上記の方法では当然ながらdevelopment環境で起動されます
本番でproduction環境を使うならRAILS_ENVを使えばいいのですが、
これを書く位置を間違えると環境が変わりませんΣ(・ω・ノ)ノ


正解はこれです

$ RAILS_ENV=production bundle exec ruby script/my_daemon start

途中や後ろにつけると環境が切り替わらなかったのでご注意を・・・*4

*1:というほど豪華なものではなく、ただの汎用性のない非同期処理系。キューにタスクが追加されるのを監視して、来たら処理するってだけ

*2:詳しくは [wikipedia:デーモン_(ソフトウェア)]

*3: 理想的には死活監視して、落ちていたら起動なのですが、そこまで必要ない規模ならこのやり方が楽かと

*4: おそらくstartと同じくスクリプトの引数として処理されている予感。スクリプト内でそれを見つけたらRailsの環境を切り替える手はあるかもしれませんが、Rails3だとやり方がわからない・・・(´-ω-)