Help us understand the problem. What is going on with this article?

Sidekiq with Padrino

More than 1 year has passed since last update.

SidekiqPadrino で使う場合のメモです

$ ruby -v
ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-darwin13.0.0]
$ padrino -v
Padrino v. 0.11.4
$ sidekiq -V
Sidekiq 2.17.0

準備 

Sidekiq はジョブキューの管理に Redisを使うのでインストールしておきます
インストール方法は割愛(mac なら brew install redis とか)

$ redis-server -v
Redis server v=2.8.1 sha=00000000:0 malloc=libc bits=64 build=b8cc45f60db4b294

あとは適当に Padrino プロジェクトを作成したり既存の Padrino プロジェクトに移動します

$ padrino g project sidekiq_with_padrino_sample -d minirecord -t rspec
$ cd sidekiq_with_padrino_sample

インストール

$ vim Gemfile
Gemfile
gem 'sidekiq'
$ bundle install

もし Bundler could not find compatible versions for gem "activesupport": のようなエラーが出た場合は Gemfile におまじないを追記します

Gemfile
gem 'activesupport’, '>= 3.2', '< 4.0'

Worker

workerを追加する

バックグラウンドで働かせたいクラスを workers ディレクトリ以下に配置して管理する方針で進めます

$ mkdir workers
$ vim worders/hard_worker.rb
workers/hard_worker.rb
class HardWorker
  include Sidekiq::Worker

  def perform(name, count)
    raise name if name == 'crash'
    logger.info "Doing hard work: #{name}"
    sleep count
  end

end

workerを使う

上で作った hard_worker をコントローラの中で使ってみます

$ bundle exec padrino g controller sample get:index
$ vim app/controllers/sample.rb
app/controllers/sample.rb
SidekiqWithPadrinoSample::App.controllers :sample, map: '/' do
  get :index do
    @count = rand(10)
    logger.info "Adding #{@count} jobs"
    @count.times do |i|
      HardWorker.perform_async("Job_#{i}", 1)
    end
  end
end

では動かしてみます

$ bundle exec padrino s
$ curl localhost:3000
   INFO -  Adding 4 jobs
NameError - uninitialized constant HardWorker:

worker が読み込めてないのでエラーになります

config/boot.rb を編集して workers ディレクトリの中身を読み込むようにします

config/boot.rb
Padrino.before_load do
  Padrino.require_dependencies Dir[Padrino.root('workers', '**', '*.rb')]
end

再度確認

$ bundle exec padrino s
$ curl localhost:3000
   INFO -  Adding 5 jobs
$ redis-cli
127.0.0.1:6379> LLEN queue:default
(integer) 5

ちゃんとキューに入ってるっぽいですね

sidekiqを起動してジョブを実行させる

このままだとジョブがキューに溜まったままで実行されないので sidekiq コマンドを叩いてジョブを処理させます

$ bundle exec sidekiq -r ./config/boot.rb
HardWorker JID-964a803489a72625b4a4562b INFO: start
HardWorker JID-964a803489a72625b4a4562b INFO: Doing hard work: job_1
HardWorker JID-43088873fa56b66813c5e6db INFO: start
HardWorker JID-43088873fa56b66813c5e6db INFO: Doing hard work: job_0
HardWorker JID-4425acb6e3b41d96b1b9c6b5 INFO: start
HardWorker JID-4425acb6e3b41d96b1b9c6b5 INFO: Doing hard work: job_2
HardWorker JID-a190428084bd67332ff6114e INFO: start
HardWorker JID-a190428084bd67332ff6114e INFO: Doing hard work: job_3
HardWorker JID-2cc0feff0ee5880daab2558f INFO: start
HardWorker JID-2cc0feff0ee5880daab2558f INFO: Doing hard work: job_4
HardWorker JID-43088873fa56b66813c5e6db INFO: done: 1.001 sec
HardWorker JID-964a803489a72625b4a4562b INFO: done: 1.004 sec
HardWorker JID-a190428084bd67332ff6114e INFO: done: 1.002 sec
HardWorker JID-2cc0feff0ee5880daab2558f INFO: done: 1.001 sec
HardWorker JID-4425acb6e3b41d96b1b9c6b5 INFO: done: 1.004 sec

キューに入っていたジョブのログがドドッと出力されます

ActiveRecord

確認用のモデルを作る

$ bundle exec padrino g model post title:string body:text
$ vim models/post.rb 
models/post.rb
class Post < ActiveRecord::Base
  # Fields
  field :title, :as => :string
  field :body,  :as => :text

  def long_method
    puts "#Long method [#{self.id}] start"
    sleep 5
    puts "#Long method [#{self.id}] finished"
  end
end

ActiveRecord 用の extension を設定する

config/boot.rb
Padrino.after_load do
  # - omit -
  ActiveRecord::Base.send(:include, Sidekiq::Extensions::ActiveRecord)
end

バックグラウンド実行してみる

Post#long_method を sidekiq の delay メソッド経由で実行させます

app/controllers/sample.rb
get :delayed_post do
  post  = Post.where(title: 'First post', body: 'body').first_or_create
  post.delay.long_method
  'enqueued'
end

確認してみます

$ bundle exec padrino s
$ curl localhost:3000/delayed_post
enqueued
$ bundle exec sidekiq -r ./config/boot.rb
#Long method [1] start
#Long method [1] finished

できました

※この手順通りにやって unable to open database file って怒られた場合は、手動で db ディレクトリを作ってみてください

Mailer

確認用のメーラーを作る

$ bundle exec padrino g mailer notifier greeting
$ vim app/mailers/notifier.rb
app/mailers/notifier.rb
SidekiqWithPadrinoSample::App.mailer :notifier do

  email :greeting do |now|
    from    'sender@example.com'
    to      'recipient@example.com'
    subject 'Hello!'

    locals now: now, hostname: `hostname`.strip
    render 'notifier/greeting'
  end

end
app/views/mailers/notifier/greeting.slim
p Hi!, it's #{now} and I'm at #{hostname}.

メーラーの設定

確認用なので、実際にメールを飛ばさないように適当に設定しておきます

app/app.rb
set :delivery_method, :test

メール送信ジョブをキューに溜める

app/controllers/sample.rb
get :email do
  SidekiqWithPadrinoSample::App.delay_for(10.seconds).deliver(:notifier, :greetings, Time.now)
  'enqueued'
end
$ bundle exec padrino s
$ curl localhost:3000/email
enqueued

sidekiq 起動用のファイルを用意する

config/sidekiq.rb
require File.expand_path("../boot.rb", __FILE__)

# Loads the Padrino applications mounted within the project.
Padrino.mounted_apps.each do |app|
  logger.info "=> Loading Padrino Application : #{app.app_class}"
  app.app_obj.setup_application!
end

キューに溜まったジョブを実行させる

$ bundle exec sidekiq -r ./config/sidekiq.rb
  …
  DEBUG -  Sending email to: recipient@example.com
  …

できました

Monitoring

sidekiq/webをマウントする

config/apps.rb
# Mount the Sidekiq monitoring application
require 'sidekiq/web'
Padrino.mount('Sidekiq::Web', app_class: 'Sidekiq::Web', app_root: Sidekiq::Web.root).to('/sidekiq')

おまじない

lib/sidekiq-web.rb
require 'sidekiq/web'
class Sidekiq::Web < ::Sinatra::Base
  class << self
    def dependencies; []; end
    def setup_application!; end
    def reload!; end
  end
end

これでOK

$ bundle exec padrino s
$ open http://localhost:3000/sidekiq

ロガーの設定

config/boot.rb
Sidekiq.logger = Padrino.logger

開発環境等では非同期にしない

config/boot.rb
require 'sidekiq/testing/inline' if PADRINO_ENV == 'development'

テスト

RSpec を使っているのであれば rspec-sidekiq を使うのがいいのかな?

Gemfile
gem 'rspec-sidekiq', :group => 'test'
spec/workers/hard_worker_spec.rb
require 'spec_helper'

describe HardWorker do
  it { expect(HardWorker).to be_processed_in :default }
end

この記事のコード

Github に置いてあります

参考

https://github.com/mperham/sidekiq/pull/760/files
http://qiita.com/nysalor/items/94ecd53c2141d1c27d1f
http://qiita.com/mat_aki/items/840b63817ea858fa09b7

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away