Resqueを利用したRailsでの非同期処理/バッチ処理
2011/03/23
Railsアプリケーションの中で非同期処理(バッチ処理)を実現したいことがあります。例えば、こんな場合です。
- ユーザーが「送信」ボタンを押したら数千通のメールを送る。
数千通のメールを送るにはかなり時間がかかるので、その処理は後回しにして、ユーザーにはすぐにレスポンスを返したいところです。
非同期処理を行うためのRubyライブラリとしてはBackgrounDRbやdelayed_jobなどが有名ですが、もう一つ有望な選択肢としてResqueというのがあることを最近知りました。
と言っても、私が知らなかっただけで、RubyGems.orgによれば11万回以上もダウンロードされている有名なライブラリです。昨年(2010年)1月に書かれた、あるブログ記事には詳しい評価が載っています。
以下、私の試用報告を書きます。なお、OSはUbuntu 10.04(LTS)、Railsのバージョンは3.0.5です。
ResqueはバックエンドとしてRedisを用います。最近話題のNoSQLデータベースの一種です。まず、これをインストールします。
$ sudo apt-get install redis-server
インストールに成功したら、念のためバージョン番号を確認します。
$ redis-cli --version redis-cli 2.2.2
続いて、動作確認のためのRailsアプリケーションを作ります。名前は適当に、kodamaとしておきましょうか。
$ rails new kodama $ cd kodama
Gemfileに必要なgemライブラリを列挙します。
source 'http://rubygems.org' gem 'rails', '3.0.5' gem 'sqlite3' gem 'resque' gem 'SystemTimer', :platform => :ruby_18
[訂正] SystemTimer は Ruby 1.9 では不要です。
gemライブラリをインストールします。
$ bundle install
config/initializers
ディレクトリにload_resque.rb
というファイルを作ります。
require 'resque' Resque.redis = 'localhost:6379'
config/routes.rb
を次のように変更します。
Kodama::Application.routes.draw do get 'hello/:text', :to => 'hello#show' end
app/controllers
ディレクトリにhello_controller.rb
というファイルを作ります。
class HelloController < ApplicationController def show Resque.enqueue(Echo, params[:text]) render :text => params[:text] end end
app
ディレクトリの下にworkers
ディレクトリを作り、そこにecho.rb
というファイルを作ります。
class Echo @queue = :default def self.perform(text) sleep 3 path = File.expand_path("log/echo.log", Rails.root) File.open(path, 'a') do |f| f.puts "Hello #{text}!" end end end
lib/tasks
ディレクトリにresque.rake
というファイルを作ります。
require 'resque/tasks'
空のログファイルを作ります。
$ touch log/echo.log
これで準備完了です。
ワーカーを起動します。
$ QUEUE=default rake environment resque:work
別のターミナルを開いて、Railsサーバを起動します。
$ rails server
さらに別のターミナルを開いて、ログファイルを監視してください。
$ tail -f log/echo.log
ここで、ブラウザで http://localhost:3000/hello/world
にアクセスします。ログファイルに注目してください。アクセスした瞬間、ブラウザには「world」と表示されますが、ログファイルには何も起こりません。しかし、約3秒数秒後に
Hello world!
と表示されます。さらに、http://localhost:3000/hello/boys
にアクセスすると、やはり約3秒数秒おいてログファイルに
Hello boys!
という行が追加されます。
うーむ。驚くほど簡単ですね!
[訂正] 本文中で2回「約3秒」と書きましたが、「数秒」と訂正します。実は、ワーカーは5秒間隔で新たなタスクが追加されていないかチェックしているので、アクセスのタイミングによって3秒後から8秒後にログファイルが書き換わります。(2011/03/23朝)
もう夜も遅いのでソースコードの説明はしませんが、何となく使い方はお分かりになるんじゃないでしょうか。
ポイントを挙げるとすれば、Echoクラスのクラスインスタンス変数@queue
にセットした:default
というシンボルです。これは何でもいいのですが、ここで指定した値が、ワーカーを起動するときの環境変数QUEUEの値になります。両者を一致させる点に注意してください。
なお、ファイル echo.rb
を置くディレクトリは、app/models
でもいいです。
書いてある通りにやってみたけど動かないよ、という方は、ワーカーを起動するコマンドを次のように変更してみてください。
$ QUEUE=default VVERBOSE=true rake environment resque:work
状況が細かく画面に表示されますので、うまく動かない原因が分かるかもしれません。