[youRoom][rails][plugin] Railsで非同期処理を行うには"Delayed_job"がおすすめ
明けましておめでとうございます。本年もよろしくお願いします。
今年初ブログは、Railsネタで始めたいと思います。
Railsで非同期処理を行いたいと思うことはありませんか?
例えば、メールの送信をオンラインで実行すると送信が終わるまでレスポンスを返せないのでユーザの待ち時間が長くなってしまいます。ユーザには画面を表示しておいて非同期でメール送信を行えれば、ユーザは早く次の画面を表示できて嬉しいですよね。
他にも、大量のDBの追加や画像の処理、他のサイトからのダウンロードなど非同期で実行できれば嬉しいケースが結構あると思います。
そんな時便利なのが"Delayed_job"というプラグインです。
http://github.com/tobi/delayed_job
このプラグインは、githubでたくさんフォークされていたり、Herokuで標準の非同期処理のライブラリになっていたりするので信頼性は高いと思います。
仕組みとしては、非常にシンプルです。
Railsのプロセスで非同期にしたい処理を、DBのJobキューを貯めるテーブルに格納しておいて、別のプロセスが定期的にレコードを監視して処理すべきレコードがあれば処理していくというものです。
ライブラリのコード自体も小さいのですべて読んで勉強するのもよいと思います。
導入方法ですが、これもシンプルです。
まずはプラグインのインストールです。
./script/plugin install git://github.com/tobi/delayed_job.git
次にJobキュー用のDBのマイグレーションファイルを作成します。
./script/generate delayed_job_migration
これで、マイグレーションファイルが生成されます。ただし、ここで作成されるマイグレーションファイルに一つ修正を加えておくことをおすすめします。作成時は、last_errorの型がstringになっているのですがRailsのエラーはstringよりも大きくなることがあるのでtextに修正しておきましょう。
そして、マイグレーションをおこないます。
rake db:migrate
これで、非同期処理の準備は整いました。では、コードを変更していきましょう。
例えば、
mail.deliver
というコードを非同期に処理したい場合は、以下のように書くのが一番簡単です。
mail.send_later(:deliver)
とするとここでは実際に実行せずに、DBのJobのキューに登録されます。このように、非常に簡単な修正で非同期処理に変更できます!!すばらしい!
ここまででは、キューに登録されたJobはまだ実行されていません。Jobを実行してみましょう。
rake jobs:work
このrakeコマンドを実行すると、非同期処理を行うプロセスが起動します。もし登録されているJobがあると順次実行されているログが出力されるでしょう。
このような感じで、"Delayed_job"を利用すると非常に簡単に非同期処理を実現できます。
最後に、おすすめのコードの修正方法も紹介しておきます。send_laterメソッドで、非同期処理にするとすべての環境で非同期処理となってしまいます。例えば開発環境では、非同期である必要はありませんよね。Jobを実行させる別プロセスを起動させるもの面倒。。。
なので、handle_asynchronouslyメソッドを用いて非同期にするのがおすすめです。僕は以下のように実装してみました。
コードは変更しないまま、以下のファイルを追加します。
config/initializers/delayed_job.rb
if %w(production).include? ::Rails.env Mail.handle_asynchronously :deliver end
こうすると、production環境のみでMail#deliverメソッドが非同期処理されるものとして登録されます。本番のコードを変更する事なしで、非同期にできるなんて素晴らしすぎますよね。ただし、すべてのインスタンスメソッドの呼び出しが非同期になってしまうので、そのあたりは注意が必要です。
あと、development環境(config.cache_classes = false)の場合Mailクラスが毎回ロードされるため非同期処理として実行されません。これで僕は、すこしハマってしまいました。。。
Railsで非同期処理を行うときは、ぜひDelayed Jobをご利用ください。
ちなみに、youRoom http://youroom.in/ では、Delayed Job を利用して非同期処理を行っています(予定)。