Resqueを使ってRailsで非同期処理を実現する

Rails で非同期処理(バックグラウンドジョブ)を実現するライブラリの Resque を使ってみた.

非同期処理のハンドリングを全て Rails で扱えるのが利点で,バックエンドは Redis で動く.さらに sinatra ベースの Resque 管理画面もデフォルトで入ってて,ジョブの状況を確認できるのが結構イケてる感じ.しかも GitHub が作ってるっていうのが驚き.

Resque の使い所としては,README.md を見るとすぐにレスポンスを返せない not always fast な処理もあるから,そういうものに Resque が使えると書いてある.例として以下の7種類が挙げられてた.まぁ他にも,画像変換をしたり,帳票を作成したりするのもそうだし,そういうちょっと重めの処理をするときにユーザのアクションを止めないで裏側で処理するときに使うライブラリって感じ.ただまぁ,README.md だけみてもすぐにコードを書ける感じじゃなくて,いろいろサンプルを探して読んでみる必要はあったけど,それでもとてもシンプルに作られていた.

  • Warming caches
  • Counting disk usage
  • Building tarballs
  • Firing off web hooks
  • Creating events in the db and pre-caching them
  • Building graphs
  • Deleting users

ちなみに,Resque の拡張機能Resque-scheduler っていうのもあって,これは Resque で制御する非同期処理を計画的に非同期で実行するためのもので,大きく2種類の計画方法がある.

  • Delayed Jobs
    • 1時間後に非同期処理を1回だけ実行,みたいな使い方をする (Linux で言う at)
  • Scheduled Jobs (Recurring Jobs)
    • 1時間に1回非同期処理を実行,みたいなスケジュールとして使う (Linux で言う crontab)

非同期アプリケーションを作ってみる

Rails アプリケーションを作る.アプリケーショ名は適当に job-resque にしておく.

$ rails new job-resque

./Gemfile に gem を追加する.

gem "resque"

Resque 用の rake を作成する.

$ rails g task resque

作成した ./lib/tasks/resque.rake でタスクを読み込む.

require 'resque/tasks'

./config/routes.rb に Resque 用の定義を追加する.

require 'resque/server'
Rails.application.routes.draw do
  get 'users/:name', :to => 'users#show'
  mount Resque::Server.new, at: "/resque"
end

サンプルとして users コントローラーを追加する.

$ rails g controller users

追加した ./app/controllers/users_controller.rb に show アクションを書く.この Resque.enqueue(Mylogger, params[:name]) というのが,Resque に非同期処理をリクエストするところ.

class UsersController < ApplicationController
  def show
    Resque.enqueue(Mylogger, params[:name])
    render :text => params[:name]
  end
end

非同期処理の Worker として mylogger.rb を追加する.

$ mkdir ./app/workers
$ vim ./app/workers/mylogger.rb

追加した ./app/workers/mylogger.rb はこんな感じ.サンプルなので何でも良くて,単純にリクエストパラメータをログに書き出すだけの処理.

class Mylogger
  @queue = :default

  def self.perform(name)
    path = File.expand_path("log/users.log", Rails.root)
    File.open(path, 'a') do |f|
      f.puts "User: #{name}"
    end
  end

end

これだけでOK.

非同期アプリケーションを起動してみる

Redis を起動して,Rails アプリケーションを起動して http://localhost:3000/users/kakakakakku にアクセスすると,View としては kakakakakku が表示されるだけだが,既にバックエンドジョブが追加されている.ちなみにこのとき Redis が起動されてないと,以下のエラーが出るので,redis-server を忘れないように.

Redis::CannotConnectError - Error connecting to Redis on 127.0.0.1:6379 (ECONNREFUSED):

Worker を起動して非同期処理をさせる

今の状態だと,非同期処理のリクエストが Redis に入っていくだけなので,Worker を起動して,非同期処理をさせてみる.

今回の非同期処理は,/users/xxx で指定した xxx をログに書き出すだけだけど,ターミナルを2枚上げて,1枚は tail -f log/users.log でログを監視する.もう1枚の方で Worker を起動する.

QUEUE=default rake environment resque:work

そうするとこんなログが書き出されるので,これで非同期処理が実現できていることがわかる.この状態で他の xxx でどんどんURLを叩いていけば,同様にログが書き出される.

User: kakakakakku

Resque 管理画面にアクセスしてみる

http://localhost:3000/resque/ にアクセスすると,Resque の管理画面にアクセスできる.動作確認のため,まずは Worker を止めておく.

デフォルトの状態だと,Jobs も Workers も0になっている.

f:id:kakku22:20140511140336p:plain

ここで http://localhost:3000/users/kakakakakku にアクセスして,管理画面を更新すると,Jobs が1になっている.

f:id:kakku22:20140511140515p:plain

Queues タブを見てみると,ちゃんと Class: Mylogger, Args: ["kakakakakku"] と非同期処理の情報が表示されている.

f:id:kakku22:20140511140536p:plain

ここで Worker を再度立ち上げて非同期処理を流した後にアクセスしてみると,非同期処理が既に行われているので,Jobs は0になって,Workers が1になっている.

f:id:kakku22:20140511140553p:plain

まとめ

Resque を使えば Rails でお手軽に非同期処理が実現できる!素晴らしい!

関連エントリー

Background Jobs with Resque - Jumpstart Lab Curriculum
GitHub製Resqueを使用したRubyでのバックグラウンド処理(バッチ処理) - Masatomo Nakano Blog
resqueとRails - Masatomo Nakano Blog
RailsでResque使い始めた - Masatomo Nakano Blog
Hello World Resque (Railsにresqueを導入する) - Qiita