Rails4でサイトを構築する – 非同期処理編(delayed_job)

「Rails4でサイトを構築する」シリーズ目次

今回は、delayed_jobを使って非同期処理を行う機能を実装してみたいと思います。

delayed_jobとは

処理に時間が掛かるようなタスクをバックグラウンドで非同期処理を実現するGemです。

同じようなものにResque gemがありますが、
delayed_jobを使っていて困ることはないので、使い慣れたdelayed_jobを使っています。

ファイルをアップロードして内容を解析してデータを保存する処理とかサイトをクローリングしてスクレイピングして情報を保存する処理等、
ブラウザから処理の実行を指示し、そのレスポンスに1分とかかかっているとサービスとして成り立たないので、このような処理に時間を要するものは、
delayed_job等を使い非同期に処理するほうがよさそうです。

delayed_jobのインストール

Gemfile

gem 'delayed_job_active_record'
gem "daemons" #これも入れないとbackground実行できない

上記を追加し、bundle installを実行
その後、

bundle exec rails g delayed_job:active_record

実行結果

create  bin/delayed_job
chmod  bin/delayed_job
create  db/migrate/20140602023844_create_delayed_jobs.rb

migrationファイルができています。基本的にそのままで大丈夫なので、

bundle exec rake db:migrate

を実行します。

sqlite3だと結果は以下のようになっています。

sqlite>.schema
CREATE TABLE "delayed_jobs" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "priority" integer DEFAULT 0 NOT NULL, "attempts" integer DEFAULT 0 NOT NULL, "handler" text NOT NULL, "last_error" text, "run_at" datetime, "locked_at" datetime, "failed_at" datetime, "locked_by" varchar(255), "queue" varchar(255), "created_at" datetime, "updated_at" datetime);
CREATE INDEX "delayed_jobs_priority" ON "delayed_jobs" ("priority", "run_at");

delayed_jobの起動と停止

ここまで行うと、delayed_jobの起動ができます。

delayed_jobの起動

#{プロジェクトディレクトリパス}/bin/delayed_job start

本当に動いたかチェック

ps aux | grep delayed_job

結果

kazuma   14382  0.4  4.5  59640 47052 ?        Sl   15:39   0:00 delayed_job                 
kazuma   14729  0.0  0.0   4404   824 pts/2    S+   15:39   0:00 grep --color=auto delayed_job

一番上がdelayed_jobのプロセスです。

delayed_jobの停止

スクレイピング処理をdelayed_jobを使って非同期化してみる

スクレイピング機能編(nokogiri)で作ったものを参考に進めて行きます。

スクレイピングした情報を保存をするためにmigrationファイルとmodelファイルを作成します。
※ 一覧とか編集とかのページを作るなら、rails g scaffoldのほうがいいかも

bundle exec rails g model Tag name:string
bundle exec rake db:migrate

controllerを作成

  • app/controllers/page_controller.rb
class PageController < ApplicationCtonroller
  def do_scrape
    Scrape.delay.bps_global_navi #非同期化したいメソッドの前にdelayを追加するだけです。
    redirect_to root, notice: '処理を開始しました。'
  end
end

config/routes.rb

  # 以下をroutes.rbに追加 
  scope :page do
    get 'do_scrape', to: 'page#do_scrape'
  end  

前回作ったlib/scrape.rbをDB保存するように改修

  • lib/scrape.rb
require 'nokogiri'
require 'open-uri'
module Scrape
  BPS_SITE_URL = '//www.bpsinc.jp'
  BPS_TARGET_CSS = '.footer-container ul.site-map-child li a' # サイト変わってた。。。ので修正

  def self.bps_global_navi
    doc = Nokogiri::HTML(open(BPS_SITE_URL))
    doc.css(BPS_TARGET_CSS).each do |link|
      Tag.find_or_create_by(name: link.content)
    end
  end
end

後は、/page/do_scrapeをたたくだけです。
delayed_jobの起動は忘れずに。

delayed_jobあるある

Q. 修正したコードが反映されません!

A. delayed_jobを再起動しましょう。

Q. 既存のサイトにアップロードしたテキストファイルをパースする部分を非同期化したらエラーになりました。

A. アップロードしたファイルはいったんどこかに保存するようにして、非同期化部分でそのファイルを読み込んでパースするように修正しましょう。

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

関連する記事

週刊Railsウォッチ

インフラ

Rubyスタイルガイドを読む

BigBinary記事より

ActiveSupport探訪シリーズ