Resqueワーカーをデーモンとして動かす

2011/04/02

先日の記事(Resqueを利用したRailsでの非同期処理/バッチ処理)の続きです。

簡単に振り返っておきましょう。今、localhost:3000でRailsアプリが動いています。ユーザーが/hello/worldにアクセスすると、すぐにレスポンスが返ってきます。しかし、数秒経過すると専用のログにテキストが書き込まれます。こういう仕組みを作りました。

この仕組みの鍵になるのが、Resqueというライブラリです。Railsアプリ側でクラスメソッドResque.enqueueを呼ぶと、非同期で実行したい処理(ジョブ)を登録できます。他方、Railsアプリとは別のプロセスでResqueワーカーというものが動いていて、数秒ごとに新しいジョブが登録されていないか監視しています。新しいジョブを見つけると、それを実行して、また監視作業に戻ります。


さて、前回はターミナルで resque:work というRakeタスクを実行することによりResqueワーカーを起動しました。開発中はこれでいいのですが、実環境ではデーモンとして実行させたいところです。デーモン(Daemon)とはバックグラウンドで動作するプログラムのことです。デーモンにすれば、起動コマンドを実行したターミナルを閉じても動き続けてくれます。CapistranoでResqueワーカーの起動・停止・再起動をしたい場合、デーモン化は必須です。

Rubyプログラムをデーモン化するのに利用できそうなライブラリとしては、daemons, daemon_controllerなどがありますが、私はdaemon-spawnを試してみることにしました。

まず、Gemfileにdaemon-spawnを追加します。

source 'http://rubygems.org'

gem 'rails', '3.0.5'
gem 'sqlite3'
gem 'resque'
gem 'SystemTimer', :platform => :ruby_18
gem 'daemon-spawn', :require => 'daemon_spawn'

インストールします。

$ bundle install

新規ファイル script/echo_server を作ります。

#!/usr/bin/env ruby

require File.expand_path('../../config/application', __FILE__)
Rails.application.require_environment!

class ResqueWorkerDaemon < DaemonSpawn::Base
  def start(args)
    @worker = Resque::Worker.new('default')
    @worker.verbose = true
    @worker.work
  end

  def stop
    @worker.try(:shutdown)
  end
end

ResqueWorkerDaemon.spawn!({
  :working_dir => Rails.root,
  :pid_file => File.join(Rails.root, 'tmp', 'pids', 'echo_server.pid'),
  :log_file => File.join(Rails.root, 'log', 'echo_server.log'),
  :sync_log => true,
  :singleton => true
})

実行権限を与えます。

$ chmod u+x script/echo_server

これで準備完了です。


デーモンを起動してみましょう。

$ script/echo_server start

プロセスIDを調べて…

$ cat tmp/pids/echo_server.pid
8508

psコマンドを実行すると…

$ ps up 8508
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
kuroda    8508  0.0  0.6 144088 26144 ?        Sl   01:45   0:00 resque-1.15

TTYの列が「?」になっているので、確かにデーモン化されています。

デーモンを停止するのは簡単です。

$ script/echo_server stop

もちろん再起動のコマンドもあります。

$ script/echo_server restart